这是一段我用来在共享内存上分配映射的代码,我正在使用boost::interprocess和托管共享内存段,现在的问题是我遇到了内存泄漏。下面给出的是最高输出。
最高输出:
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1. 27594 tpmon 20 0 46132 2140 1664 S 0.0 0.0 0:00.00 test_stub
2. 27594 tpmon 20 0 46132 2176 1664 S 0.0 0.0 0:00.01 test_stub
3. 27594 tpmon 20 0 46264 2248 1664 S 0.0 0.0 0:00.01 test_stub
4. 27594 tpmon 20 0 46264 2280 1664 S 0.0 0.0 0:00.01 test_stub
从顶部输出可以明显看出,驻留内存在不断增加,在共享内存映射中,我只有下面列出的条目,如三元组:
Deb0 0 150520 Deb1 1 150520 Deb10 10 150520 Deb11 11 150520 Deb12 12 150520 Deb13 13 150520 Deb14 14 150520 Deb15 15 150520 Deb16 16 150520 Deb17 17 150520 Deb18 18 150520 Deb19 19 150520 Deb2 2 150520 Deb20 20 150520 Deb21 21 150520 Deb22 22 150520 150520 Deb24 24 150520 Deb25 25 150520 Deb26 26 150520 Deb27 27 150520 Deb28 28 150520 Deb29 29 150520 Deb3 3 150520 Deb4 4 150520 Deb5 5 150520 Deb6 6 150520 Deb7 7 150520 Deb8 8 150520 Deb9 9 150520
这些不会再添加了,只会更新。
我采取的下一步是按以下方式运行valgrind:
从valgrind输出中可以明显看出,分配的数目大于可用的数目,但这具有任何实际含义,如果是共享内存,我们希望分配的内存在进程退出后出现。
test_stub.cxx
#include <stdio.h>
#include <iostream>
#include<string>
#include <sstream>
using namespace std;
int main() {
TickerInfoManager * ticker_info_manager_inst = TickerInfoManager::get_instance("test");
char_allocator ca(ticker_info_manager_inst->get_managed_memory_segment().get_allocator<char>());
while(1) {
for( int i=0; i < 30; i++ ) {
basic_time now;
stringstream convert;
convert << i;
int curr_time = now.fullTime();
ticker_info_manager_inst->put_records( *(new tickerUpdateInfo(const_cast<char*>(("Deb"+convert.str()).c_str()), i, curr_time, ca ) ));
}
sleep(1);
}
//ticker_info_manager_inst->print_contents();
return 0;
}
TickerInfoManager.cxx
#include <TickerInfoManager.h>
#include <TickerInfoMangerImplementation.h>
TickerInfoManager::TickerInfoManager( const sharedMemoryNameT & name) : pInfoMangerImpl( new tickerInfoMangerImplementation( name )) {
}
TickerInfoManager* TickerInfoManager::get_instance( const sharedMemoryNameT & name ) {
return (new TickerInfoManager( name ) );
}
bool TickerInfoManager::put_records( const tickerUpdateInfoT & record ) {
return pInfoMangerImpl->put_records( record );
}
void TickerInfoManager::print_contents() {
return pInfoMangerImpl->print_contents();
}
bip::managed_shared_memory& TickerInfoManager::get_managed_memory_segment() {
return pInfoMangerImpl->get_managed_memory_segment();
}
TickerInfoMangerImplementation.cxx
#include <TickerInfoMangerImplementation.h>
#include <boost/interprocess/managed_shared_memory.hpp>
#include <iostream>
#include "basic_time.h"
using namespace boost::interprocess;
tickerInfoMangerImplementation::tickerInfoMangerImplementation( const sharedMemoryNameT & name ): m_name(name),
m_managed_memory_segment( open_or_create, "test", 1000000 ),
p_ticker_info_map( m_managed_memory_segment.find_or_construct<KeyTickerCountMap>("TickerInfoMap")(std::less<SharedString>(), m_managed_memory_segment.get_segment_manager() ) ) {
std::cout<<"Finished initializing TickerInfo Manager" << std::endl;
}
bool tickerInfoMangerImplementation::put_records( const tickerUpdateInfoT & record ) {
//If the key has not been inserted, insert the key and update the map
KeyTickerCountMap::iterator iterator_to_map = p_ticker_info_map->find( record.m_id );
if( iterator_to_map == p_ticker_info_map->end() ) {
p_ticker_info_map->emplace( record.m_id, std::make_pair( record.m_total_ticker_count, record.m_active_ticker_count ) );
}
else {
p_ticker_info_map->at(record.m_id) = std::make_pair( record.m_total_ticker_count, record.m_active_ticker_count) ;
}
//record.m_ca.deallocate( const_cast<char*> ((record.m_id).c_str()), record.m_id.length() );
return true;
}
int tickerInfoMangerImplementation::calculate_historical_time_using_threshold( const thresholdT seconds ) {
basic_time::Secs_t secs( seconds );
basic_time tick_time;
tick_time -= secs;
return ( tick_time.fullTime() );
}
void tickerInfoMangerImplementation::print_contents() {
KeyTickerCountMap::iterator map_iter = (*p_ticker_info_map).begin();
KeyTickerCountMap::iterator map_end = (*p_ticker_info_map).end();
for ( ; map_iter != map_end; ++map_iter ) {
std::cout<< map_iter->first << " " << map_iter->second.first << " " << map_iter->second.second << std::endl;
}
}
TickerInfo.h
#ifndef __TICKER_INFO__
#define __TICKER_INFO__
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/containers/string.hpp>
#include <iostream>
typedef boost::interprocess::managed_shared_memory::allocator<char>::type char_allocator;
typedef boost::interprocess::basic_string<char, std::char_traits<char>, char_allocator> shm_string;
//Data to insert in shared memory
typedef struct tickerUpdateInfo {
shm_string m_id;
int m_total_ticker_count;
int m_active_ticker_count;
char_allocator m_ca;
tickerUpdateInfo( char * id,
int total_ticker_count,
int active_ticker_count,
const char_allocator &a )
: m_id( id, a),
m_total_ticker_count(total_ticker_count),
m_active_ticker_count(active_ticker_count),
m_ca(a) {
}
~tickerUpdateInfo() {
std::cout<< "Calling destructor" <<std::endl;
}
tickerUpdateInfo& operator=(const tickerUpdateInfo& other) {
if (this != &other) {
m_total_ticker_count = other.m_total_ticker_count;
m_active_ticker_count = other.m_active_ticker_count;
}
return *this;
}
} tickerUpdateInfoT;
#endif
**TickerInfoManager.h**
#ifndef __TICKER_INFO_MANAGER__
#define __TICKER_INFO_MANAGER__
#include <TickerInfoManagerConstants.h>
#include <TickerInfoMangerImplementation.h>
//class tickerInfoMangerImplementation;
class TickerInfoManager {
public:
static TickerInfoManager* get_instance( const sharedMemoryNameT & name );
bool put_records( const tickerUpdateInfoT & record );
TickerInfoManager( const sharedMemoryNameT & name);
void print_contents();
boost::interprocess::managed_shared_memory& get_managed_memory_segment();
private:
std::auto_ptr<tickerInfoMangerImplementation> pInfoMangerImpl;
};
#endif
TickerInfoMangerImplementation.h
#ifndef __TICKER_INFO_MANAGER_IMPL__
#define __TICKER_INFO_MANAGER_IMPL__
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/containers/string.hpp>
#include <boost/interprocess/containers/map.hpp>
//#include <TickerInfoManagerConstants.h>
#include <TickerInfo.h>
#include <vector>
#include <fire/HashMap.h>
#include <string>
typedef std::string sharedMemoryNameT;
typedef int thresholdT;
namespace bip = boost::interprocess;
//the strings also need to be assigned from the shared memory
typedef bip::allocator<void, bip::managed_shared_memory::segment_manager> VoidAllocator;
typedef bip::allocator<char, bip::managed_shared_memory::segment_manager> CharAllocator;
typedef bip::basic_string<char, std::char_traits<char>, CharAllocator> SharedString;
//Note that map<Key, MappedType>'s value_type is std::pair<const Key, MappedType>,
//so the allocator must allocate that pair.
typedef bip::allocator<std::pair<const SharedString, std::pair<int,int> >, bip::managed_shared_memory::segment_manager> MapValueTypeAllocator;
typedef bip::map<SharedString, std::pair<int,int>, std::less<SharedString>, MapValueTypeAllocator> KeyTickerCountMap;
//allocator for the string
typedef bip::allocator<SharedString, bip::managed_shared_memory::segment_manager> StringAllocator;
class tickerInfoMangerImplementation {
public:
tickerInfoMangerImplementation( const sharedMemoryNameT & name );
bool put_records( const tickerUpdateInfoT & record );
void print_contents();
bip::managed_shared_memory& get_managed_memory_segment() {
return m_managed_memory_segment;
}
private:
const sharedMemoryNameT m_name;
bip::managed_shared_memory m_managed_memory_segment;
bip::offset_ptr<KeyTickerCountMap> p_ticker_info_map;
int calculate_historical_time_using_threshold( const thresholdT seconds );
};
#endif
所以基本上,代码流是这样的:
从test_stub.cxx中,我们在TickerInfoManager中调用put_records ------>调用put_records(在tickerInfoManagerImplementation中)---> tickerInfoManagerImplementation中的put_records将数据插入到位于共享内存中的映射中。
如果有人想重现这种情况,我已经添加了完整的代码。
我的问题是我该如何调试该问题,我是否不正确理解valgrind输出?
谢谢,
黛布
最佳答案
我知道这听起来不太好,但是也许您应该停止在C++中这样做。
至少停止尝试模仿Java。停止编写线程不安全的代码。停止写入内存泄漏。
怎么样?从简单开始。
您的目标太高了。
我已经为您提供了约4次协助
在这两个最后的答案中,我基本上都重写了整个内容,并建议您重新考虑设计。
但是,您仍然可以返回已经存在的代码的/ further /复杂版本。从最重要的开始:
*new
中使用内存泄漏运算符(main()
)¹:*(new tickerUpdateInfo(const_cast<char *>(("Deb" + convert.str()).c_str()), i, curr_time, ca)));
恭喜,因为您还在同一行中滥用了
const_cast
。只需已接受std::string
即可,不要邪恶。 除此之外,代码还存在很多问题:
tickerUpdateInfo::operator=
通过不分配id(和分配器)而违反了值语义。 (不使用该运算符)tickerUpdateInfo
类的目的,只是要确保id
字符串的分配和分配超出了必要的范围(哦,当然要泄漏,请参见第一个项目符号)。 typedef struct X {...} X_t;
是一种C-ism。无需在C++ tickerUpdateInfo
不需要保留分配器(它是字符串的属性)。 TickerUpdateManager
具有get_instance
。那么,是单例吗?哦,不,不是因为每次调用它都会返回一个新实例。哦,经理也被泄露了。 get_segment_manager
)。那是一个界面设计的禁忌和违反《法则》。Manager / Implementation的区别不过是噪音,而增加了复杂性
TicketUpdateManagerImplementation
的构造不能保护共享内存或带锁的TickerInfoMap
的构造。 auto_ptr
已弃用。使用std::unique_ptr
(或必须使用boost::scoped_ptr
)。当然,这是假设您甚至在那儿需要pimpl。 m_name
(甚至不用于打开共享内存段),而sharedMemoryNameT
却是蛇油。 std::less<SharedString>
是已经存在的默认比较器(*p_ticker_info_map)[record.m_id] = { record.m_total_ticker_count, record.m_active_ticker_count };
优先选择简洁的代码,而不是带有流控制逻辑的重复代码。另外,为什么会有
true
返回值? calculate_historical_time_using_threshold
是一个有趣的名称。该函数不属于此类,并且不计算历史时间。哦,它不使用任何阈值。 (AFAICT它将返回全时seconds
之前的代码。)shm_string
与SharedString
(及其各自的分配器)的typedef进行竞争。 清理
这是对管理器的建议清理,正确完成了 pimpl 。看看我们如何在 header 中使用标准库类型:
#pragma once
#include <string>
#include <memory>
class TickerInfoManager {
public:
TickerInfoManager(const char* name);
~TickerInfoManager();
void put(std::string const& id, int total, int active);
void print_contents() const;
private:
struct Impl;
std::unique_ptr<Impl> pimpl_;
};
现在,主程序同样简单得多:
#include <iostream>
#include <string>
#include "TickerInfoManager.hxx"
#include "basic_time.h"
#include <thread>
int main() {
TickerInfoManager tickerinfo("test");
while (1) {
for (int i = 0; i < 30; i++) {
tickerinfo.put("Deb" + std::to_string(i), i, basic_time().fullTime());
}
std::this_thread::sleep_for(std::chrono::milliseconds(100)); // sleep(1)
std::cout << "." << std::flush;
}
}
该实现通过IPC同步实现了线程安全,并集中了所有与Boost Interprocess相关的东西:
#include "TickerInfoManager.hxx"
#include <iostream>
#include <boost/interprocess/sync/interprocess_mutex.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/containers/string.hpp>
#include <boost/interprocess/containers/map.hpp>
#include <boost/thread/lock_guard.hpp>
namespace /*anon*/ {
namespace bip = boost::interprocess;
typedef bip::allocator<char, bip::managed_shared_memory::segment_manager> CharAllocator;
typedef bip::basic_string<char, std::char_traits<char>, CharAllocator> SharedString;
typedef bip::allocator<std::pair<SharedString const, std::pair<int, int> >, bip::managed_shared_memory::segment_manager> MapValueTypeAllocator;
typedef bip::map<SharedString, std::pair<int, int>, std::less<SharedString>, MapValueTypeAllocator> KeyTickerCountMap;
typedef boost::lock_guard<bip::interprocess_mutex> lock_guard;
struct LockableMap {
LockableMap(MapValueTypeAllocator alloc) : map(alloc) {}
KeyTickerCountMap map;
bip::interprocess_mutex mutex;
};
}
struct TickerInfoManager::Impl {
Impl(const char* name)
: m_segment(bip::open_or_create, name, 1000000),
m_alloc(m_segment.get_segment_manager()),
p_data(m_segment.find_or_construct<LockableMap>("TickerInfoMap")(m_segment.get_segment_manager()))
{}
bip::managed_shared_memory m_segment; // order is relevant
CharAllocator m_alloc;
bip::offset_ptr<LockableMap> p_data;
KeyTickerCountMap& map() { return p_data->map; }
bip::interprocess_mutex& mutex() { return p_data->mutex; }
};
TickerInfoManager::TickerInfoManager(const char* name) : pimpl_(new Impl(name)) { }
TickerInfoManager::~TickerInfoManager() { }
void TickerInfoManager::put(std::string const& id, int total, int active) {
SharedString shid(id.begin(), id.end(), pimpl_->m_alloc);
lock_guard lock(pimpl_->mutex());
pimpl_->map()[shid] = { total, active };
}
void TickerInfoManager::print_contents() const {
lock_guard lock(pimpl_->mutex());
for (auto const& e : pimpl_->map())
std::cout << e.first << " " << e.second.first << " " << e.second.second << std::endl;
}
总而言之,它少于代码量的一半,而实际上
参见代码 Compiling On Coliru
¹C++ program crashes after creating 2000 objects
关于c++ - boost::interprocess 和valgrind,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/26661835/