这是一段我用来在共享内存上分配映射的代码,我正在使用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次协助

  • Keeping fixed size symbols in shared memory
  • Program crashes when trying to access the inner map
  • Using boost multi index like relational DB

  • 在这两个最后的答案中,我基本上都重写了整个内容,并建议您重新考虑设计。

    但是,您仍然可以返回已经存在的代码的/ 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。那么,是单例吗?哦,不,不是因为每次调用它都会返回一个新实例。哦,经理也被泄露了。
  • 管理器不执行而不执行。它所做的只是委托(delegate)给一个实现。 Pimpl惯用法是不错的方法,但是如果实际上将接口(interface)与实现隔离开,则它仅用于的任何目的。但是,Manager会直接在接口(interface)中公开实现类型,甚至直接将非const引用公开给实现(get_segment_manager)。那是一个界面设计的禁忌和违反《法则》。

    Manager / Implementation的区别不过是噪音,而增加了复杂性
  • TicketUpdateManagerImplementation的构造不能保护共享内存或带锁的TickerInfoMap的构造。
  • auto_ptr已弃用。使用std::unique_ptr(或必须使用boost::scoped_ptr)。当然,这是假设您甚至在那儿需要pimpl。
  • 从未使用过m_name(甚至不用于打开共享内存段),而sharedMemoryNameT却是蛇油。
  • std::less<SharedString>是已经存在的默认比较器
  • put_records是一种极其round回的操作方式:
    (*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_stringSharedString(及其各自的分配器)的typedef进行竞争。
  • 无需将所有实现细节(typedefs)放入全局 namespace 和头文件中。

  • 清理

    这是对管理器的建议清理,正确完成了 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/

    10-09 03:37