我是在访问已经释放的内存,还是在这种情况下 DrMemory 报告不正确?

Am I accessing already freed memory, or is DrMemory reporting incorrectly in this case?

我有以下程序:

#include <vector>

struct Comp1
{
    float x;
    std::vector<int> vec;
};

int main()
{
    std::vector<char> data;

    data.resize(sizeof(Comp1));

    Comp1* ptr1 = new (&data[0]) Comp1({.3f,{3,4,2,1}});

    data.resize(sizeof(Comp1)*2);

    Comp1* ptr2 = new (&data[sizeof(Comp1)]) Comp1({.2f,{2,3,4}});

    ptr1->~Comp1();

    std::vector<char>::iterator itrStart = data.begin();
    std::vector<char>::iterator itrEnd = itrStart + sizeof(Comp1);
    data.erase(itrStart,itrEnd);

    ptr2 = reinterpret_cast<Comp1*>(&data[0]);

    ptr2->~Comp1();

    return 0;
}

编译没有问题。在我的机器上执行没有问题。 运行 它通过 GDB,没有任何报告。我的理解是我在上面所做的一切都是正确的(除了当我从 char*Comp1* reinterpret_cast 时我错过了 std::launder 的使用?).

但是,通过以下方式调用此程序的 DrMemory:

drmemory.exe test.exe -callstack_max_frames 40 -malloc_max_frames 40 -free_max_frames 40

给我以下错误输出:

Dr. Memory version 2.2.0 build 1 built on Jul  1 2019 00:42:20
Windows version: WinVer=105;Rel=1909;Build=18363;Edition=Enterprise
Dr. Memory results for pid 20424: "test.exe"
Application cmdline: "bin/test.exe -callstack_max_frames 40 -malloc_max_frames 40 -free_max_frames 40"
Recorded 117 suppression(s) from default C:\Program Files (x86)\Dr. Memory\bin\suppress-default.txt

Error #1: UNADDRESSABLE ACCESS of freed memory: reading 0x01975230-0x01975234 4 byte(s)
# 0 std::vector<>::~vector               [C:/MSYS2-32/mingw32/include/c++/10.1.0/bits/stl_vector.h:680]
# 1 Comp1::~Comp1                        [C:/Tests/test.cpp:3]
# 2 main                                 [C:/Tests/test.cpp:21]
Note: @0:00:01.329 in thread 2068
Note: next higher malloc: 0x01975258-0x01975268
Note: 0x01975230-0x01975234 overlaps memory 0x01975228-0x01975238 that was freed here:
Note: # 0 replace_operator_delete                              [d:\drmemory_package\common\alloc_replace.c:2975]
Note: # 1 __gnu_cxx::new_allocator<>::deallocate               [C:/MSYS2-32/mingw32/include/c++/10.1.0/ext/new_allocator.h:133]
Note: # 2 std::allocator_traits<>::deallocate                  [C:/MSYS2-32/mingw32/include/c++/10.1.0/bits/allocator.h:187]
Note: # 3 std::_Vector_base<>::_M_deallocate                   [C:/MSYS2-32/mingw32/include/c++/10.1.0/bits/stl_vector.h:354]
Note: # 4 std::vector<>::_M_default_append                     [C:/MSYS2-32/mingw32/include/c++/10.1.0/bits/vector.tcc:675]
Note: # 5 std::vector<>::resize                                [C:/MSYS2-32/mingw32/include/c++/10.1.0/bits/stl_vector.h:940]
Note: instruction: mov    0x04(%edx) -> %ecx

Error #2: UNADDRESSABLE ACCESS of freed memory: reading 0x0197522c-0x01975230 4 byte(s)
# 0 std::vector<>::~vector               [C:/MSYS2-32/mingw32/include/c++/10.1.0/bits/stl_vector.h:680]
# 1 Comp1::~Comp1                        [C:/Tests/test.cpp:3]
# 2 main                                 [C:/Tests/test.cpp:21]
Note: @0:00:01.447 in thread 2068
Note: next higher malloc: 0x01975258-0x01975268
Note: 0x0197522c-0x01975230 overlaps memory 0x01975228-0x01975238 that was freed here:
Note: # 0 replace_operator_delete                              [d:\drmemory_package\common\alloc_replace.c:2975]
Note: # 1 __gnu_cxx::new_allocator<>::deallocate               [C:/MSYS2-32/mingw32/include/c++/10.1.0/ext/new_allocator.h:133]
Note: # 2 std::allocator_traits<>::deallocate                  [C:/MSYS2-32/mingw32/include/c++/10.1.0/bits/allocator.h:187]
Note: # 3 std::_Vector_base<>::_M_deallocate                   [C:/MSYS2-32/mingw32/include/c++/10.1.0/bits/stl_vector.h:354]
Note: # 4 std::vector<>::_M_default_append                     [C:/MSYS2-32/mingw32/include/c++/10.1.0/bits/vector.tcc:675]
Note: # 5 std::vector<>::resize                                [C:/MSYS2-32/mingw32/include/c++/10.1.0/bits/stl_vector.h:940]
Note: instruction: mov    (%edx) -> %edx

Error #3: UNADDRESSABLE ACCESS of freed memory: reading 0x01975234-0x01975238 4 byte(s)
# 0 std::_Vector_base<>::~_Vector_base               [C:/MSYS2-32/mingw32/include/c++/10.1.0/bits/stl_vector.h:336]
# 1 std::vector<>::~vector                           [C:/MSYS2-32/mingw32/include/c++/10.1.0/bits/stl_vector.h:683]
# 2 Comp1::~Comp1                                    [C:/Tests/test.cpp:3]
# 3 main                                             [C:/Tests/test.cpp:21]
Note: @0:00:01.471 in thread 2068
Note: next higher malloc: 0x01975258-0x01975268
Note: 0x01975234-0x01975238 overlaps memory 0x01975228-0x01975238 that was freed here:
Note: # 0 replace_operator_delete                              [d:\drmemory_package\common\alloc_replace.c:2975]
Note: # 1 __gnu_cxx::new_allocator<>::deallocate               [C:/MSYS2-32/mingw32/include/c++/10.1.0/ext/new_allocator.h:133]
Note: # 2 std::allocator_traits<>::deallocate                  [C:/MSYS2-32/mingw32/include/c++/10.1.0/bits/allocator.h:187]
Note: # 3 std::_Vector_base<>::_M_deallocate                   [C:/MSYS2-32/mingw32/include/c++/10.1.0/bits/stl_vector.h:354]
Note: # 4 std::vector<>::_M_default_append                     [C:/MSYS2-32/mingw32/include/c++/10.1.0/bits/vector.tcc:675]
Note: # 5 std::vector<>::resize                                [C:/MSYS2-32/mingw32/include/c++/10.1.0/bits/stl_vector.h:940]
Note: instruction: mov    0x08(%eax) -> %edx

Error #4: UNADDRESSABLE ACCESS of freed memory: reading 0x0197522c-0x01975230 4 byte(s)
# 0 std::_Vector_base<>::~_Vector_base               [C:/MSYS2-32/mingw32/include/c++/10.1.0/bits/stl_vector.h:336]
# 1 std::vector<>::~vector                           [C:/MSYS2-32/mingw32/include/c++/10.1.0/bits/stl_vector.h:683]
# 2 Comp1::~Comp1                                    [C:/Tests/test.cpp:3]
# 3 main                                             [C:/Tests/test.cpp:21]
Note: @0:00:01.489 in thread 2068
Note: next higher malloc: 0x01975258-0x01975268
Note: 0x0197522c-0x01975230 overlaps memory 0x01975228-0x01975238 that was freed here:
Note: # 0 replace_operator_delete                              [d:\drmemory_package\common\alloc_replace.c:2975]
Note: # 1 __gnu_cxx::new_allocator<>::deallocate               [C:/MSYS2-32/mingw32/include/c++/10.1.0/ext/new_allocator.h:133]
Note: # 2 std::allocator_traits<>::deallocate                  [C:/MSYS2-32/mingw32/include/c++/10.1.0/bits/allocator.h:187]
Note: # 3 std::_Vector_base<>::_M_deallocate                   [C:/MSYS2-32/mingw32/include/c++/10.1.0/bits/stl_vector.h:354]
Note: # 4 std::vector<>::_M_default_append                     [C:/MSYS2-32/mingw32/include/c++/10.1.0/bits/vector.tcc:675]
Note: # 5 std::vector<>::resize                                [C:/MSYS2-32/mingw32/include/c++/10.1.0/bits/stl_vector.h:940]
Note: instruction: mov    (%eax) -> %ecx

Error #5: UNADDRESSABLE ACCESS of freed memory: reading 0x0197522c-0x01975230 4 byte(s)
# 0 std::_Vector_base<>::~_Vector_base               [C:/MSYS2-32/mingw32/include/c++/10.1.0/bits/stl_vector.h:335]
# 1 std::vector<>::~vector                           [C:/MSYS2-32/mingw32/include/c++/10.1.0/bits/stl_vector.h:683]
# 2 Comp1::~Comp1                                    [C:/Tests/test.cpp:3]
# 3 main                                             [C:/Tests/test.cpp:21]
Note: @0:00:01.504 in thread 2068
Note: next higher malloc: 0x01975258-0x01975268
Note: 0x0197522c-0x01975230 overlaps memory 0x01975228-0x01975238 that was freed here:
Note: # 0 replace_operator_delete                              [d:\drmemory_package\common\alloc_replace.c:2975]
Note: # 1 __gnu_cxx::new_allocator<>::deallocate               [C:/MSYS2-32/mingw32/include/c++/10.1.0/ext/new_allocator.h:133]
Note: # 2 std::allocator_traits<>::deallocate                  [C:/MSYS2-32/mingw32/include/c++/10.1.0/bits/allocator.h:187]
Note: # 3 std::_Vector_base<>::_M_deallocate                   [C:/MSYS2-32/mingw32/include/c++/10.1.0/bits/stl_vector.h:354]
Note: # 4 std::vector<>::_M_default_append                     [C:/MSYS2-32/mingw32/include/c++/10.1.0/bits/vector.tcc:675]
Note: # 5 std::vector<>::resize                                [C:/MSYS2-32/mingw32/include/c++/10.1.0/bits/stl_vector.h:940]
Note: instruction: mov    (%eax) -> %edx

===========================================================================
FINAL SUMMARY:

DUPLICATE ERROR COUNTS:

SUPPRESSIONS USED:

ERRORS FOUND:
      5 unique,     5 total unaddressable access(es)
      0 unique,     0 total uninitialized access(es)
      0 unique,     0 total invalid heap argument(s)
      0 unique,     0 total GDI usage error(s)
      0 unique,     0 total handle leak(s)
      0 unique,     0 total warning(s)
      0 unique,     0 total,      0 byte(s) of leak(s)
      0 unique,     0 total,      0 byte(s) of possible leak(s)
ERRORS IGNORED:
      2 potential error(s) (suspected false positives)
         (details: C:\Users\david\AppData\Roaming\Dr. Memory\DrMemory-test.exe.20424.000\potential_errors.txt)
      8 unique,    14 total,  19443 byte(s) of still-reachable allocation(s)
         (re-run with "-show_reachable" for details)
Details: C:\Users\david\AppData\Roaming\Dr. Memory\DrMemory-test.exe.20424.000\results.txt

我的程序是不是遗漏了什么,或者这是 DrMemory 无法真正解决我正在做的事情的情况?

在你的代码中

data.resize(sizeof(Comp1)*2);

这是向量使用的存储的修改事件。发生这种情况时,向量中的所有指针、迭代器和引用都将失效。也就是说

ptr1->~Comp1();

正在访问一个无效对象并且您有未定义的行为。这不是内存泄漏,而是非法访问问题,这就是 Dr. Memory 正在诊断的问题。

(评论太长了。)

如果您希望就地构建的对象的后备存储缓冲区不移动,您可以使用字节向量的向量。如果你想要连续的内存(比如,缓存局部性),这将不起作用。

#include <vector>

using std::vector;

struct Comp1 {
    float x;
    vector<int> vec;
};

int main() {
    vector<vector<char>> data;

    data.push_back(vector<char>(sizeof(Comp1)));
    Comp1* ptr1 = new (&data[0][0]) Comp1({.3f,{3,4,2,1}});

    data.push_back(vector<char>(sizeof(Comp1)));

    Comp1* ptr2 = new (&data[1][0]) Comp1({.2f,{2,3,4}});

    ptr1->~Comp1();

    auto itrStart = data.begin();
    auto itrEnd = itrStart + 1;
    data.erase(itrStart, itrEnd);

    ptr2->~Comp1();
}