deb*_*deb 2 c++ memory boost valgrind
这是我用来在共享内存上分配映射的一段代码,我使用的是 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
Run Code Online (Sandbox Code Playgroud)
从顶部输出可以明显看出常驻内存在不断增加,在共享内存映射中,我只有下面列出的条目,作为三元组:
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 Deb23 23 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 7 Deb7 150520 Deb8 8 150520 9 Deb9 150520
这些不会再被添加,它们只会得到更新。
我采取的下一步是按如下方式运行 valgrind:
Run Code Online (Sandbox Code Playgroud)sudo -u tpmon valgrind --tool=memcheck --leak-check=yes ./bin/test_stub And below is the output: ==21404== Memcheck, a memory error detector ==21404== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al. ==21404== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info ==21404== Command: ./bin/test_stub ==21404== Finished initializing TickerInfo Manager ^C==21404== ==21404== HEAP SUMMARY: ==21404== in use at exit: 60,627 bytes in 1,264 blocks ==21404== total heap usage: **5,059 allocs, 3,795 frees**, 812,123 bytes allocated ==21404== ==21404== 29 bytes in 1 blocks are possibly lost in loss record 2 of 7 ==21404== at 0x4A075BC: operator new(unsigned long) (vg_replace_malloc.c:298) ==21404== by 0x3A7149C3C8: std::string::_Rep::_S_create(unsigned long, unsigned long, std::allocator<char> const&) (in /usr/lib64/libstdc++.so.6.0.13) ==21404== by 0x3A7149CDE4: ??? (in /usr/lib64/libstdc++.so.6.0.13) ==21404== by 0x3A7149CF32: std::basic_string<char, std::char_traits<char>, > std::allocator<char> >::basic_string(char const*, std::allocator<char> const&) (in /usr/lib64/libstdc++.so.6.0.13) ==21404== by 0x40986F: main (test_stub.cxx:12)
从 valgrind 输出很明显,allocs 的数量大于 free,但这有什么实际意义,在共享内存的情况下,我们希望分配的内存在进程退出后出现。
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;
}
Run Code Online (Sandbox Code Playgroud)
代码信息管理器.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();
}
Run Code Online (Sandbox Code Playgroud)
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;
}
}
Run Code Online (Sandbox Code Playgroud)
股票信息.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
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
所以基本上代码是这样的:
从 test_stub.cxx 中,我们在 TickerInfoManager 中调用 put_records ------> 调用 put_records(在tickerInfoManagerImplementation 中)---> 在tickerInfoManagerImplementation 中的 put_records 将数据插入到驻留在共享内存中的映射中。
如果有人想重现这种情况,我已经添加了完整的代码。
我的问题是如何调试这个问题,我是不是没有正确理解 valgrind 输出?
谢谢,黛布!
警告:在前面咆哮。我确实得到了建设性,并在最后提供了一个建设性的固定版本。
我知道这听起来不太好,但也许你应该停止在 C++ 中这样做。
至少停止尝试模仿 Java。停止编写线程不安全的代码。停止写入内存泄漏。
你这里的目标太高了。
我已经帮助了你大约 4 次了
在这最后两个答案中,我基本上重写了整个内容并建议您重新考虑设计。
然而,您设法返回已经 [...] 代码的 /further/ 复杂版本。从最重要的开始:
您正在使用内存泄漏运算符 ( *new)¹ in main():
*(new tickerUpdateInfo(const_cast<char *>(("Deb" + convert.str()).c_str()), i, curr_time, ca)));
Run Code Online (Sandbox Code Playgroud)
恭喜你,因为你也在const_cast同一行被虐了。只是采取std::strings,不要作恶。
您的共享内存访问都没有受到锁的适当保护。这意味着您的进程的所有行为都是未定义的。
除此之外,代码还有很多问题:
tickerUpdateInfo::operator=通过不分配 id(和分配器)违反了值语义。(不使用此运算符)
事实上,我没有看到整个tickerUpdateInfo类的目的,除了确保id字符串被分配和释放超过必要的(哦,当然会被泄露,请参阅第一个项目符号)。
typedef struct X {...} X_t;是C主义。在 C++ 中不需要标记结构
tickerUpdateInfo 不需要持有分配器(它是字符串的一个属性)。
你TickerUpdateManager有get_instance。那么,它是单例吗?哦不,不可能是因为它每次被调用时都会返回一个新实例。哦,经理也被泄露了。
经理什么都不做。它所做的只是委托给一个实现。Pimpl 惯用语很好,但只有当它真正将接口与实现隔离时才有任何用途。然而,你的 Manager 直接暴露了接口中的实现类型,甚至直接暴露了非常量引用到实现 ( get_segment_manager) 中。这是界面设计禁忌和迪米特法则违反。
这种管理器/实现的区别只不过是噪音并增加了复杂性
的构造TicketUpdateManagerImplementation不保护共享内存或TickerInfoMap带锁的构造。
auto_ptr已弃用。使用std::unique_ptr(或者boost::scoped_ptr如果你必须。)。当然,这是假设您甚至需要那里的疙瘩。
m_name从不使用(甚至不用于打开共享内存段),并且sharedMemoryNameT是蛇油。
std::less<SharedString> 已经是默认比较器
put_records 是一种非常迂回的做法:
(*p_ticker_info_map)[record.m_id] = { record.m_total_ticker_count, record.m_active_ticker_count };
Run Code Online (Sandbox Code Playgroud)
与流控制逻辑的重复相比,更喜欢简洁的代码。另外,为什么会有true返回值?
calculate_historical_time_using_threshold是一个有趣的名字。该函数不属于此类,也不计算历史时间。哦,它不使用任何阈值。(AFAICT 它在全职seconds前返回。)
你有竞争的类型定义shm_stringvs. SharedString(带有它们各自的分配器)。
无需将所有实现细节(typedef)放入全局命名空间和头文件中。
这是对经理的建议清理,pimpl做得对。看看我们如何在头文件中使用标准库类型:
#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_;
};
Run Code Online (Sandbox Code Playgroud)
主程序现在同样简单得多:
#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;
}
}
Run Code Online (Sandbox Code Playgroud)
该实现通过 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;
}
Run Code Online (Sandbox Code Playgroud)
总而言之,它的代码量不到一半,而它确实
查看在 Coliru 上编译的代码