2014-10-30 73 views
1

這裏是我用來分配共享內存映射的一段代碼,我使用boost :: interprocess和managed shared segment segment,現在問題是我遇到了內存泄漏。以下給出的是最高產出。boost interprocess和valgrind

頂部輸出:

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 Deb23 23 150520 Deb24 24 150520 Deb25 25 150520 Deb26 26 150520 Deb27 27 150520 Deb28 28 150520 Deb29 29 150520 D eb3 3 150520 Deb4 4 150520 Deb5 5 150520 Deb6 6 150520 Deb7 7 150520 Deb8 8 150520 Deb9 9 150520

這些不會被添加任何進一步,他們只是得到更新。

我採取的下一個步驟是運行的valgrind如下:

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人數比免費較大,但確實有任何真正的意義,在共享內存的情況下,我們希望分配的內存在進程退出後出現。

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我們稱之爲put_records在TickerInfoManager ---- - >調用put_records(在tickerInfoManagerImplementation中)---> put_records在tickerInfoManagerImplementation中將數據插入駐留在shar中的映射編輯記憶。

我已經添加了完整的代碼,如果有人想重現這種情況。

我的問題是我該如何去調試這個問題,我不理解valgrind輸出正確嗎?

謝謝, Deb!

回答

4

警告:前方咆哮。我確實具有建設性,並在最後提供了一個建設性的固定版本。

我知道這不會聽起來不錯,但也許你應該停止在C++中這樣做。

至少不要試圖模仿Java。停止編寫線程不安全的代碼。停止編寫內存泄漏。

怎麼樣? 開始簡單

你在這裏的目標太高了。

我協助你的4倍左右,現在

在這兩個最後的答案,我基本上重寫了整個事情,並建議你重新思考設計。

然而,你已經設法回來了/已經[...]代碼的更多/複雜版本。最重要的開始:

  • 您所使用的內存泄漏運營商(*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不需要保存分配器(它是字符串的屬性)。

  • 您的TickerUpdateManagerget_instance。那麼,這是單身嗎?哦,不,它不可能是因爲每次調用它時會返回一個新實例。哦,經理也被泄露了。

  • 經理確實沒有什麼。它所做的只是委託給一個實現。 Pimpl成語很好,但如果它實際上將接口與實現隔離開來,它只能用於任何的用途。然而,您的管理器直接在接口中公開實現類型,甚至直接將非常量引用公開爲實現(get_segment_manager)。這是一個界面設計禁止和Demeter違規。

    該經理/執行的區別是什麼,但噪音和增加了複雜性

  • TicketUpdateManagerImplementation建設不警惕共享內存的建設或TickerInfoMap有鎖。

  • auto_ptr已棄用。使用std::unique_ptr(如果必須,請使用boost::scoped_ptr)。當然,這是假設你甚至需要那裏的pimpl。

  • 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 }; 
    

    不想在具有流控邏輯重複簡潔的代碼。另外,爲什麼有一個true返回值?

  • calculate_historical_time_using_threshold是一個有趣的名字。該函數不屬於這個類,並且不計算曆史時間。哦,它並沒有使用任何閾值。 (AFAICT它返回全職seconds前。)

  • 你有競爭typedefs爲shm_stringSharedString(與他們各自的分配器)。

  • 沒有必要把所有的實現細節(typedefs)放到全局命名空間和頭文件中。

清理

這裏的經理的建議清理,與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_; 
}; 

主程序同樣是簡單得多的現在:

的實現是線程安全與IPC同步和集中所有的升壓間相關的東西:

#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

+0

哇,然而在這四種情況中,你沒有得到任何感謝或顯然有任何upvote(op的唯一回復是對其他內容的間接思考,並且任何當前的+1都是我的)。不知道你爲什麼打擾?有耐心解釋的榮譽 - 爲了其他可能真正聽的人的利益 - 爲什麼他們不應該這樣編碼。 ;-) – 2016-02-03 00:19:47

+1

@underscore_d老實說,我主要是回答學習自己。改進(設計)現有代碼是一門藝術。讓其他人從事其他工作的人員可以繼續工作是一項挑戰。我不是一個天賦的人才。我必須投入工作才能讓這些事情變得更好:) – sehe 2016-02-03 00:52:32

相關問題