是否可以将一段内存标记为“越界”?所以堆管理器不会从中分配?
今天早些时候,我问了这个问题。
在花了一些时间调查这个问题后,我发现了发生了什么。我将其作为一个新问题发布,因为我认为它足够有趣,可以作为一个单独的问题进行跟踪。我将用答案(以及指向此问题的链接)更新该问题。
从调试器启动单元测试
// Construct object
Object* pObject = new Object(...);
// Pointer value of pObject == 0x05176960
// Lots of other code
// ...
// Destroy object
delete pObject;
// Construct object again
pObject = new Object(...);
// Pointer value of pObject == 0x05560194 /* Different memory location */
从命令行启动单元测试
// Construct object
Object* pObject = new Object(...);
// Pointer value of pObject == 0x05176960
// Lots of other code
// ...
// Destroy object
delete pObject;
// Construct object again
pObject = new Object(...);
// Pointer value of pObject == 0x05176960 /* Same memory location */
总之:
- 从命令行启动单元测试时,后续调用
new
分配一个对象
(在分配新对象之前删除
之前的对象
)总是返回相同的 内存中的地址。 - 从调试器启动单元测试时,后续调用
new
来分配对象
(删除
之前的分配新对象之前的对象
)始终返回内存中的唯一地址。
问题是,因为通过命令行启动时对象的分配总是在内存中获得相同的地址,所以我正在访问的存储了旧指针的映射仍然可以使用并且测试不会崩溃。但我希望我的单元测试在缺陷修复未到位时崩溃,以确保它不会默默地失败并且缺陷不会再次出现。
我的问题有两个部分:
为什么堆管理器在从命令行启动单元测试时会重用同一部分内存,但当我从调试器启动单元测试时却不会?
是否有我可以在测试工具上使用的编译器设置,或者我可以调用的方法来防止堆管理器重新使用我已删除的内存部分,以允许我正确编写单元测试? 1
1Obviously one way of doing this is to not delete the original object, but the part of the code that allocates this is in my production code, and I doing this would result in memory leaks.
Earlier today I asked this question.
After spending some time investigating this issue, I have discovered what is going on. I am posting this as a new question because I think it is interesting enough to track as a separate issue. I will update that question with the answer (and a link to this one).
Launching unit test from debugger
// Construct object
Object* pObject = new Object(...);
// Pointer value of pObject == 0x05176960
// Lots of other code
// ...
// Destroy object
delete pObject;
// Construct object again
pObject = new Object(...);
// Pointer value of pObject == 0x05560194 /* Different memory location */
Launching unit test from command line
// Construct object
Object* pObject = new Object(...);
// Pointer value of pObject == 0x05176960
// Lots of other code
// ...
// Destroy object
delete pObject;
// Construct object again
pObject = new Object(...);
// Pointer value of pObject == 0x05176960 /* Same memory location */
In summary:
- When launching the unit test from the command line, subsequent calls to
new
to allocate anObject
(delete
ing the previousObject
before allocating a new one) always return the same address in memory. - When launching the unit test from the debugger, subsequent calls to
new
to allocate anObject
(delete
ing the previousObject
before allocating a new one) always return a unique address in memory.
The problem is that because allocations of Object
always get the same address in memory when launching through the command line, a map which I am accessing which has stored the old pointer can still be used and the test won't crash. But I want my unit test to crash when the defect fix is not in place, to ensure that it doesn't silently fail and the defect doesn't come back.
There are 2 parts to my question:
Why would the heap manager re-use the same part of memory when launching a unit test from the command line, but not when I launch the unit test from the debugger?
Is there a compiler setting I could use on my test harness, or a method I can call to prevent the heap manager from re-using a section of memory that I have deleted, to allow me to correctly write my unit test? 1
1Obviously one way of doing this is to not delete the original object, but the part of the code that allocates this is in my production code, and I doing this would result in memory leaks.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
您的单元测试有缺陷,因为它依赖于未定义的行为。您应该重写单元测试,以便它不依赖于未定义的行为,在这种情况下,无论内存管理器决定如何分配内存,它总是会通过。
您正在做的是这样的:
您应该重新构建单元测试,使其工作方式如下:
Your unit test is flawed, since it's relying on undefined behavior. You should rewrite your unit test so that it doesn't rely on undefined behavior, in which case it will always pass regardless of how the memory manager decides to allocate memory.
What you're doing is this:
You should instead restructure the unit test so it works like this:
您可以将
new
和delete
替换为具有所需行为的您自己的版本。You could replace
new
anddelete
with your own versions that have the behaviour you want.首先 - 不在“正常”内存管理器中。一旦释放内存,您就将其所有权传递给内存管理器,后者可以重用它。
您可以 按照用户 Andreas Brinck 建议编写自定义管理器,但是什么会吗?它不会从空气中制造内存,而是从 CRT 堆或操作系统堆等地方请求内存。
场景 A。它不会将内存返回到底层堆 - 将会发生泄漏,并且内存块仍将被映射到地址空间并且可以访问。
场景 B。它会将内存返回到底层堆 - 然后当您的管理员尝试再次分配内存时,底层堆可以再次返回该块。此外,当您将内存返回给底层堆时,您也不知道底层堆会做什么。它可能会使其未映射或未映射 - 因此访问该内存可能会崩溃或不会。
底线是你完蛋了。尝试测试未定义的行为不会很有成效。
First of all - not in a "normal" memory manager. Once you deallocate memory you pass ownership of it to the memory manager and the latter can reuse it.
You could write a custom manager as user Andreas Brinck suggests, but what would it do? It doesn't craft memory from air, it requests it from somewhere like CRT heap or operating system heap.
Scenario A. It wouldn't return memory to the underlying heap - you'll have a leak and the memory block will still be mapped into the address space and it will be accessible.
Scenario B. It will return memory to underlying heap - then when your mananger tries to allocate memory again the underlying heap can return that block again. Also you don't know what the underlying heap does when you return memory to it. It might make it unmapped or not - so accessing that memory might crash or not.
The bottom line is you're screwed. Trying to test undefined behavior is not going to be very productive.
这是未定义行为的示例。 C++ 或堆管理器都没有定义如何分配内存。您不能依赖内存被重用或不被重用。当您执行上述操作时,无法确定或更改返回的指针是否与第一个分配的指针不同。
This is an example of UNDEFINED BEHAVIOUR. Neither C++ or the heap manager define how memory is going to be allocated. You cannot rely on either memory being reused or not being reused. When you do something like in the above, there is no way to determine or change whether or not the pointer returned will be different from the first allocated.