2011-11-29 26 views
8

Tôi có một chương trình, một phần để ghi nhật ký thông tin, tôi xuất tên của một số lớp khi chúng được sử dụng (cụ thể là tôi thêm một mục vào nhật ký nói dọc theo dòng Messages::CSomeClass transmitted to 127.0.0.1). Tôi làm điều này với mã tương tự như sau:Rò rỉ bộ nhớ sau khi sử dụng typeinfo :: name()

std::string getMessageName(void) const { 
    return std::string(typeid(*this).name()); 
} 

Và vâng, trước khi bất cứ ai chỉ nó ra, tôi nhận ra rằng đầu ra của typeinfo::name là thực hiện cụ thể.

Theo MSDN

Các type_info::name chức năng thành viên trả về một const char* thành một chuỗi null-chấm dứt đại diện cho tên con người có thể đọc được các loại. Bộ nhớ được trỏ đến được lưu trong bộ nhớ cache và không bao giờ được trực tiếp deallocated.

Tuy nhiên, khi tôi thoát khỏi chương trình của mình trong trình gỡ lỗi, việc sử dụng "mới" typeinfo::name() sẽ hiển thị dưới dạng rò rỉ bộ nhớ. Nếu tôi xuất thông tin cho 2 lớp, tôi nhận được 2 rò rỉ bộ nhớ, v.v. Điều này gợi ý rằng dữ liệu được lưu trữ không bao giờ được giải phóng.

Trong khi đây không phải là vấn đề lớn, có vẻ lộn xộn và sau một phiên gỡ lỗi dài, nó có thể dễ dàng ẩn các rò rỉ bộ nhớ chính hãng.

Tôi đã xem xét và tìm thấy một số thông tin hữu ích (một câu trả lời SO cung cấp một số thông tin thú vị về how typeinfo may be implemented), nhưng tôi tự hỏi nếu bộ nhớ này bình thường được giải phóng bởi hệ thống, hoặc nếu có điều gì đó tôi có thể làm "không thông báo" các rò rỉ khi gỡ lỗi.

Tôi có một kế hoạch sao lưu, để tự viết mã phương pháp getMessageName và không dựa vào typeinfo::name, nhưng tôi vẫn muốn biết nếu có điều gì đó tôi đã bỏ lỡ.

+1

Có thể có liên quan? [http://connect.microsoft.com/VisualStudio/feedback/details/106937/memory-leaks-reported-by-debug-crt-inside-typeinfo-name](http://connect.microsoft.com/VisualStudio/ feedback/details/106937/bộ nhớ-rò rỉ-báo cáo-by-debug-crt-bên trong-typeinfo-tên). Bạn đang sử dụng trình biên dịch nào? Có thể thử một trình biên dịch khác nếu có thể? –

+0

Vì nó được lưu trữ, đừng lo lắng về nó. – Jagannath

+0

@jagansai: Tôi không lo lắng về sự rò rỉ của chính nó, vì nó chỉ ảnh hưởng đến đầu ra của trình gỡ lỗi khi thoát ứng dụng - mối quan tâm của tôi là nó có thể che giấu các rò rỉ bộ nhớ * thực *. Và có vẻ lộn xộn. Tôi làm như đầu ra trình gỡ rối gọn gàng :) – icabod

Trả lời

4

giải pháp khác là để sửa chữa các vấn đề tiềm ẩn. Điều này không thực sự là một rò rỉ bộ nhớ, chỉ là một báo cáo sai. Các khối bộ nhớ được cấp phát cho tyepinfo() và chuỗi name() được gán kiểu khối sai. Nó có lẽ không phải là một ý tưởng tốt để "tự do" bộ nhớ này, vì một nỗ lực sẽ được thực hiện bởi CRT để giải phóng nó một lần nữa. Tin tốt là điều này cuối cùng đã được sửa trong VS2012 (_MSC_VER 1700+).

Vì điều này chỉ áp dụng cho các bản dựng _DEBUG, sau đây có thể là giải pháp an toàn hơn. Hàm _FixTypeInfoBlockUse() nên được gọi như đã đề cập ở trên ngay trước khi thoát khỏi điểm vào mô-đun (chính, WinMain, v.v.).

#if defined(_DEBUG) && (_MSC_VER >= 1000 && _MSC_VER <= 1699) 
// 
// Debug memory block header: 
// o Borrowed from the Microsoft CRT to fix the false "memory leak" report 
//  when using typeinfo 'name' accessor in a _DEBUG build of the library. 
// 
struct _CrtMemBlockHeader 
    { 
    struct _CrtMemBlockHeader * pBlockHeaderNext; 
    struct _CrtMemBlockHeader * pBlockHeaderPrev; 
    char *      szFileName; 
    int       nLine; 
    #ifdef _WIN64 
    int       nBlockUse; 
    size_t      nDataSize; 
    #else 
    size_t      nDataSize; 
    int       nBlockUse; 
    #endif 
    long      lRequest; 
    unsigned char    gap[4]; 
    }; 

static void __cdecl _FixTypeInfoBlockUse(void) 
    { 
    __type_info_node* pNode = __type_info_root_node._Next; 

    while(pNode != NULL) 
     { 
     __type_info_node* pNext = pNode->_Next; 

     (((_CrtMemBlockHeader*)pNode) - 1)->nBlockUse = _CRT_BLOCK; 

     if (pNode->_MemPtr != NULL) 
     (((_CrtMemBlockHeader*)pNode->_MemPtr) - 1)->nBlockUse = _CRT_BLOCK; 

     pNode = pNext; 
     } 
    } 

#endif//defined(_DEBUG) && (_MSC_VER >= 1000 && _MSC_VER <= 1699) 
+0

Thay đổi câu trả lời được chấp nhận của tôi cho điều này, vì nó sạch hơn một chút (?) So với các câu trả lời khác, một phần là do '# if' giới hạn nó chỉ với các bản dựng bị ảnh hưởng. – icabod

1

Như đã chỉ ra bởi Chris Parton trong các nhận xét, điều này có vẻ là known bug, ít nhất với phiên bản trình biên dịch tôi đang sử dụng - nâng cấp lên VC11 sẽ khắc phục sự cố, nếu tôi có thể nâng cấp.

Cố gắng để xóa đầu ra của typeinfo::name() một phần hoạt động:

std::string getMessageName(void) const 
{ 
    std::string typeStr(typeid(*this).name()); 
    delete (typeid(*this).name()); 
    return typeStr; 
} 

Tuy nhiên vẫn còn một số lỗ hổng bộ nhớ - Tôi chỉ nhận thấy rằng trước đây tôi xuất hiện để được nhận hai rò rỉ cho mỗi cuộc gọi (có lẽ do các lớp học là bên trong một không gian tên?). Sử dụng các phiên bản mã trên, điều này đã đi xuống một rò rỉ cho mỗi cuộc gọi.

Một giải pháp khác dường như hoạt động là liên kết trong phiên bản động của thư viện MFC (có, tôi đang sử dụng MFC, đừng phán xét tôi), thay vì phiên bản tĩnh.

+0

Chấp nhận câu trả lời của riêng tôi khi không có bất kỳ người nào khác. – icabod

+0

Có, bạn bị rò rỉ vì bạn đã xóa bộ nhớ chỉ cho một chuỗi. Ngoài ra còn có nút danh sách liên kết đơn tương ứng sẽ bị xóa. Xem câu trả lời của tôi. – Stas

+0

Đây là một giải pháp thực sự xấu vì chuỗi được lưu trữ trong bộ nhớ vì vậy nếu bạn sẽ gọi typeid (* this) .name() một lần nữa sau khi xóa, bạn sẽ nhận được một chuỗi rác. –

4

Tôi vừa tình cờ gặp vấn đề này khi cố gắng xóa nhật ký của VLD. Có, đây là known bug, chỉ được sửa trong VC11. Nó tồn tại trong các phiên bản trước của MSVC bao gồm 2010. Lỗi này chỉ xuất hiện nếu bạn sử dụng MFC. Nếu bạn sử dụng MFC làm DLL thay vì thư viện tĩnh, rò rỉ bộ nhớ sẽ vẫn tồn tại, nhưng sẽ không bị phát hiện.

Có một bộ nhớ cache toàn cầu của type_info tên và nó không được xóa (các đoạn trích từ <typeinfo>):

struct __type_info_node { 
    void *_MemPtr; 
    __type_info_node* _Next; 
}; 

extern __type_info_node __type_info_root_node; 

Ý tưởng là để xóa bộ nhớ cache này. Chức năng này hoạt động cho tôi:

#include <typeinfo> 

void clear_type_info_cache() 
{ 
    __type_info_node* & node = __type_info_root_node._Next; 
    while(node) 
    { 
     if (node->_MemPtr) 
     { 
     delete node->_MemPtr; 
     } 
     __type_info_node* tempNode = node; 
     node = node->_Next; 
     delete tempNode; 
    } 
} 

Gọi clear_type_info_cache() trước khi thoát. Bạn có thể đăng ký với atexit

#include <cstdlib> 

int WinMain(...) 
{ 
    atexit(&clear_type_info_cache); 
    ... 
} 

hoặc gọi nó ngay lập tức trước khi rời khỏi WinMain

struct dummy_scope_exit 
{ 
    typedef void (*Fun)(); 
    dummy_scope_exit(Fun f) : m_f(f) {} 
    ~dummy_scope_exit() { m_f(); } 
    Fun m_f; 
}; 

int WinMain(...) 
{ 
    dummy_scope_exit cleaner = &clear_type_info_cache; 
    ... 
} 
0

VS lưu trữ thông tin loại trong danh sách được liên kết đơn lẻ. Tiêu đề của danh sách này có thể truy cập bằng cấu trúc mờ có thể truy cập theo tên __type_info_root_node. Trên thực tế nó là một cấu trúc SLIST_HEADER.

Win32 API có một bộ chức năng an toàn đồng thời để làm việc với các cấu trúc như vậy. Để sửa báo cáo rò rỉ bộ nhớ Trong trường hợp của bạn, bạn cần xóa tất cả các nút trong danh sách này.

#include <Windows.h> 
#include <typeinfo> 
#include <vld.h> 

void ClearTypeinfoCache() 
{ 
#ifdef _DEBUG 
    while (auto entry = InterlockedPopEntrySList(reinterpret_cast<PSLIST_HEADER>(&__type_info_root_node))) 
    { 
     free(entry); 
    } 
#endif 
} 

int main() 
{ 
    atexit(ClearTypeinfoCache); 
    return 0; 
} 

Cập nhật: VLD 2.5.1 không báo cáo rò rỉ bộ nhớ trên type_info :: name() trong VS2015 Cập nhật 3.