簡體   English   中英

保持展示位置刪除跟蹤新位置

[英]keep the track of placement delete against placement new

我正在開發一種像內存泄漏檢測器的工具。 我可以跟蹤新的展示位置,但我如何跟蹤展示位置刪除。 我做了很多研發,我發現放置刪除不能直接調用,它在異常時由構造函數調用。 那么我如何才能跟蹤位置刪除對新的展示位置?

任何幫助,將不勝感激......

您想要分配和解除分配:

  • malloc /免費
  • 新/刪除(“常規”表格)
  • new [] / delete []

但是你與新的配對有什么關系呢? (明確地說:采用void *且通常簡稱為“placement new”的那個,而不是new的其他放置形式。)它不是刪除,而是顯式的析構函數調用。

  • T *p = new(mem) T(); / p->~T()

Placement new實際上並沒有分配任何東西,它只是調用構造函數的語法糖。 您不需要也不應該跟蹤它。 它甚至比其他形式更怪異,因為首先調用“destroy”位然后用另一個替換被破壞的對象(與其他形式的序列相反)並不罕見:

{
  T some_object;

  some_object->~T(); // die! die! die!
  new(&some_object) T();  // didn't save the return value? memory leak..? nope.
} // some_object leaves scope and is destructed (again)

你沒有,沒有什么可以追蹤的。

Placement new表示對象放在內存位置。 它的存儲已經分配。 您不會在對象本身上調用任何形式的刪除,因為這是您從中獲取存儲的工作。

就是這樣:

void* memory = operator new(sizeof(int)); // A
new (memory) int; // B
operator delete(memory); // C

A和C是你應該跟蹤的,而不是B.

1)對新的alloc使用placement new

2)使用一個包裝函數(如) delete_object ,它通過你的分配器調用,一個用於delete_array 請注意,指針的對齊方式可能會偏離您返回的實際分配(對於數組)。

首先,不是OP而是其他讀者:據我所知,OP 並不是在討論在預分配存儲中構建對象。

我不是在談論這個。

對OP:你沒有,只需讓你的operator delete形式轉發給普通的operator delete 畢竟這是將為任何成功構造的動態分配對象調用的釋放函數。 無論如何你都需要支持它。

如果要將調試信息與分配的內存關聯以便在釋放函數中使用,那么一個實際的選擇是分配比請求更多的位,並將信息放在該塊的開頭或結尾,返回指向未使用部分的指針。 然后operator delete需要做相反的事情。 注意:對齊。

我猜一個不切實際的選擇是使用靜態std::map (或其他關聯數組,如哈希表)。 它運行在線程安全問題等,但它避免了對齊問題。


附錄 ,一個完整的例子:

// Note: while this example works nicely, it doesn't support threading.
#include <iostream>
#include <new>          // std::bad_alloc
#include <stddef.h>     // ptrdiff_t, size_t, max_align_t
#include <stdlib.h>     // malloc, EXIT_*,

typedef unsigned char Byte;

struct Source_reference
{
    char const* filename;
    int         line_number;
};

#define SOURCE_REF  Source_reference{ __FILE__, __LINE__ }

auto operator<<( std::ostream& stream, Source_reference const& ref )
    -> std::ostream&
{
    if( ref.filename == nullptr )
    {
        return stream << "[unknown source location]";
    }
    return stream << "\"" << ref.filename << "\"@" << ref.line_number;
}

struct Block_info
{
    Source_reference    source_ref;
    Block_info*         p_prev;
    Block_info*         p_next;

    void link_in_after( Block_info& predecessor )
    {
        p_prev = &predecessor;
        p_next = predecessor.p_next;
        predecessor.p_next = this;
        p_next->p_prev = this;
    }

    void unlink()
    {
        p_next->p_prev = p_prev;
        p_prev->p_next = p_next;
    }
};

namespace g {
    size_t const    max_align   = sizeof( max_align_t );
    size_t const    prefix_size =
        ((sizeof( Block_info ) + max_align - 1)/max_align)*max_align;
    Block_info      block_list_header   =
        {{nullptr,0}, &block_list_header, &block_list_header};
}  // namespace g

auto tracking_alloc( size_t const n_bytes_requested )
    -> void*
{
    size_t const n_bytes = n_bytes_requested + g::prefix_size;
    Byte* const result = static_cast<Byte*>( malloc( n_bytes ) );
    if( !result ) { throw std::bad_alloc(); }

    Block_info* const p_info = ::new( result ) Block_info();
    p_info->link_in_after( g::block_list_header );

    return result + g::prefix_size;
}

void tracking_dealloc( void* p )
{
    Block_info* p_info  = reinterpret_cast<Block_info*>(
        static_cast<Byte*>( p ) - g::prefix_size
        );
    p_info->unlink();
    free( p_info );
}

auto operator new( size_t const n_bytes )
    -> void*
{ return tracking_alloc( n_bytes ); }

auto operator new[]( size_t const n_bytes )
    -> void*
{ return operator new( n_bytes ); }

void operator delete( void* p )
{ tracking_dealloc( p ); }

void operator delete[]( void* p )
{ operator delete( p ); }

auto operator new( size_t const n_bytes, Source_reference const& ref )
    -> void*
{
    Byte* const p               = static_cast<Byte*>( operator new( n_bytes ) );

    Block_info* const p_info    = reinterpret_cast<Block_info*>( p - g::prefix_size );
    p_info->source_ref = ref;

    return p;
}

void operator delete( void* p, Source_reference const& )
{
    using namespace std;
    cout << "!placement delete called." << endl;
    operator delete( p );
}

void list_blocks()
{
    using namespace std;
    cout << "Known allocated blocks:" << endl;
    for(
        Block_info* p_info = g::block_list_header.p_next;
        p_info != &g::block_list_header;
        p_info = p_info->p_next
        )
    {
        void* const p_data = reinterpret_cast<Byte*>( p_info ) + g::prefix_size;
        cout
            << "- Basic allocation " << p_data
            << " from " << p_info->source_ref << "."
            << endl;
    }
    cout << "- End list." << endl;
}

#include <vector>
auto main()
    -> int
{
    using namespace std;

    int* p = new( SOURCE_REF ) int( 42 );
    cout << "An int allocated with ref at " << p << "." << endl;
    list_blocks();

    int* p2 = new int( 43 );
    cout << "\nAn int allocated sans ref at " << p << "." << endl;
    list_blocks();

    {
        vector<double> v( 3 );
        cout << "\nA vector constructed" << endl;
        list_blocks();

        try
        {
            struct Ungood{ Ungood() { throw 666; } };
            cout << "\nAllocating with ref an object that fails construction." << endl;
            new( SOURCE_REF ) Ungood;
        }
        catch( ... )
        {}
        list_blocks();

        delete p;
        cout << "\nThe int-with-ref deleted." << endl;
        list_blocks();
    }
    cout << "\nThe vector destroyed" << endl;
    list_blocks();

    delete p2;
    cout << "\nThe int-sans-ref deleted." << endl;
    list_blocks();
}

使用MinGW g ++ 4.8.2輸出:

An int allocated with ref at 0x213c0.
Known allocated blocks:
- Basic allocation 0x213c0 from "foo.cpp"@134.
- End list.

An int allocated sans ref at 0x213c0.
Known allocated blocks:
- Basic allocation 0x21410 from [unknown source location].
- Basic allocation 0x213c0 from "foo.cpp"@134.
- End list.

A vector constructed
Known allocated blocks:
- Basic allocation 0x21460 from [unknown source location].
- Basic allocation 0x21410 from [unknown source location].
- Basic allocation 0x213c0 from "foo.cpp"@134.
- End list.

Allocating with ref an object that fails construction.
!placement delete called.
Known allocated blocks:
- Basic allocation 0x21460 from [unknown source location].
- Basic allocation 0x21410 from [unknown source location].
- Basic allocation 0x213c0 from "foo.cpp"@134.
- End list.

The int-with-ref deleted.
Known allocated blocks:
- Basic allocation 0x21460 from [unknown source location].
- Basic allocation 0x21410 from [unknown source location].
- End list.

The vector destroyed
Known allocated blocks:
- Basic allocation 0x21410 from [unknown source location].
- End list.

The int-sans-ref deleted.
Known allocated blocks:
- End list.

我對你的一個例子有評論:

MyClass(char * c)
{
  a = new char[10];
  strcpy(a, c);
  throw here ...
}
~MyClass()
{
  delete[] a;
}

我認為你的問題在於在構造函數中使用new而不將其包裝在某種資源管理器中。 如果構造函數拋出,無論對象是如何在其中新建的(new或placement new),分配給它的內存都將泄漏,除非它由另一個對象管理。

struct Janitor {
    Janitor(char* item) : mgr(item) {}
    ~Janitor() { if uncaught_exception() delete [] mgr; }
    char *mgr;
};

MyClass(char * c)
{
   Janitor j(new char[10]);
        // j is destroyed both if the rest of the contstructor succeeds
        // and if it throws

   //unsafe code
   strcpy(j.mgr, c);
   ...

   //at the end
   //pass the resource management to MyClass
   a = j.mgr;
};

其中一些也可能有用。
http://www.gotw.ca/gotw/008.htm
http://www.gotw.ca/gotw/056.htm
http://www.gotw.ca/gotw/010.htm
http://www.gotw.ca/gotw/022.htm
http://www.gotw.ca/gotw/042.htm

在基類的接口中聲明operator new/new[]/delete/delete[] ,(以及內置變體或隱藏它們)。 然后實現你需要的那些。

如果你想要做的就是跟蹤特定類層次結構(或集合)中對象的泄漏,這應該可用於某些目的。

插圖(注意,偽代碼如下):

/* option 1) use a specified allocator for all class instances */
namespace { const char* const TypeID("MON::t_object"); }

void* MON::t_object::operator new(size_t size) {
    void* const allocation(AllocatorForType(TypeID).operator_new(size));
    std::cout << "MON::t_object::operator new - size: " << size << " TypeID: " << TypeID << " address: " << allocation << "\n";
    return allocation;
}

/* option 2) use a specific allocator via placement, or type overload */
void* MON::t_object::operator new(size_t size, t_allocator& allocator) {
    void* const allocation(allocator.operator_new(size));
    std::cout << "MON::t_object::operator new - size: " << size << " allocator: " << & allocator << " address: " << allocation << "\n";
    return allocation;
}

void MON::t_object::operator delete(void* allocation) {

    std::cout << "MON::t_object::operator delete: " << allocation << "\n";

    /* now call through your allocator interface */
    if ( /* using option 1 */ ) {
        AllocatorForType(TypeID).operator_delete(allocation);
    }
    else /* using option 2 */ {
        AllocatorForAllocationWithAddress(allocation).operator_delete(allocation);
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM