一个链表的释放能否装各种绘图方法,怎么释放才不泄漏

VC &&&&最新内容
VC &&&&相关内容深入分析Object.finalize方法的实现原理 - ImportNew
“物有本末,事有始终。知其先后,则近道矣”
如果类中重写了finalize方法,当该类对象被回收时,finalize方法有可能会被触发,下面通过一个例子说明finalize方法对垃圾回收有什么影响。
public class FinalizeCase {
private static Block holder =
public static void main(String[] args) throws Exception {
holder = new Block();
System.gc();
//System.in.read();
static class Block {
byte[] _200M = new byte[200*];
Block类中声明一个占用内存200M的数组,是为了方便看出来gc之后是否回收了Block对象,执行完的gc日志如下:
从gc日志中可以看出来,执行完System.gc()之后,Block对象被如期的回收了,如果在Block类中重写了finalize方法,会是一样的结果么?
static class Block {
byte[] _200M = new byte[200*];
protected void finalize() throws Throwable {
System.out.println(&invoke finalize&);
执行完成gc日志如下:
和之前的gc日志进行比较,发现finalize方法确实被触发了,但是Block对象还在内存中,并没有被回收,这是为什么?
下面对finalize方法的实现原理进行分析。
finalize实现原理
《》一文中分析了Java对象创建的整个过程,代码实现如下:
对象的初始化过程会对has_finalizer_flag和RegisterFinalizersAtInit进行判断,如果类重写了finalize方法,且方法体不为空,则调用register_finalizer函数,继续看register_finalizer函数的实现:
其中Universe::finalizer_register_method()缓存的是jdk中java.lang.ref.Finalizer类的register方法,实现如下:
在jvm中通过JavaCalls::call触发register方法,将新建的对象O封装成一个Finalizer对象,并通过add方法添加到Finalizer链表头。
对象O和Finalizer类的静态变量unfinalized有联系,在发生GC时,会被判定为活跃对象,因此不会被回收
FinalizerThread线程
在Finalizer类的静态代码块中会创建一个FinalizerThread类型的守护线程,但是这个线程的优先级比较低,意味着在cpu吃紧的时候可能会抢占不到资源执行。
FinalizerThread线程负责从ReferenceQueue队列中获取Finalizer对象,如果队列中没有元素,则通过wait方法将该线程挂起,等待被唤醒
如果返回了Finalizer对象,执行对象的runFinalizer()方法,其实可以发现:在runFinalizer()方法中主动捕获了异常,即使在执行finalize方法抛出异常时,也没有关系。
通过hasBeenFinalized方法判断该对象是否还在链表中,并将该Finalizer对象从链表中删除,这样下次gc时就可以把原对象给回收掉了,最后调用了native方法invokeFinalizeMethod,其中invokeFinalizeMethod方法最终会找到并执行对象的finalize方法。
ReferenceHandler线程
有个疑问:既然FinalizerThread线程是从ReferenceQueue队列中获取Finalizer对象,那么Finalizer对象是在什么情况下才会被插入到ReferenceQueue队列中?
Finalizer的祖父类Reference中定义了ReferenceHandler线程,实现如下:
当pending被设置时,会调用ReferenceQueue的enqueue方法把Finalizer对象插入到ReferenceQueue队列中,接着通过notifyAll方法唤醒FinalizerThread线程执行后续逻辑,实现如下:
pending字段什么时候会被设置?
在GC过程的引用处理阶段,通过oopDesc::atomic_exchange_oop方法把发现的引用列表设置在pending字段所在的地址
Finalizer导致的内存泄漏
平常使用的Socket通信,SocksSocketImpl的父类重写了finalize方法
这么做主要是为了确保在用户忘记手动关闭socket连接的情况下,在该对象被回收时能够自动关闭socket来释放一些资源,但是在开发过程中,真的忘记手动调用了close方法,那么这些socket对象可能会因为FinalizeThread线程迟迟没有执行到这些对象的finalize方法,而导致一直占用某些资源,造成内存泄露。
MinHeapFreeRatio
MaxHeapFreeRatio这两个参数在JDK8中没有起作用
关于ImportNew
ImportNew 专注于 Java 技术分享。于日 11:11正式上线。是的,这是一个很特别的时刻 :)
ImportNew 由两个 Java 关键字 import 和 new 组成,意指:Java 开发者学习新知识的网站。 import 可认为是学习和吸收, new 则可认为是新知识、新技术圈子和新朋友……
新浪微博:
推荐微信号
反馈建议:ImportNew.
广告与商务合作QQ:
– 好的话题、有启发的回复、值得信赖的圈子
– 写了文章?看干货?去头条!
– 为IT单身男女服务的征婚传播平台
– 优秀的工具资源导航
– 活跃 & 专业的翻译小组
– 国内外的精选博客文章
– UI,网页,交互和用户体验
– JavaScript, HTML5, CSS
– 专注Android技术分享
– 专注iOS技术分享
– 专注Java技术分享
– 专注Python技术分享
& 2018 ImportNew如何检测内存泄漏 - 文章 - 伯乐在线
& 如何检测内存泄漏
初识 Visual Leak Detector
灵活自由是C/C++语言的一大特色,而这也为C/C++程 序员出了一个难题。当程序越来越复杂时,内存的管理也会变得越加复杂,稍有不慎就会出现内存问题。内存泄漏是最常见的内存问题之一。内存泄漏如果不是很严 重,在短时间内对程序不会有太大的影响,这也使得内存泄漏问题有很强的隐蔽性,不容易被发现。然而不管内存泄漏多么轻微,当程序长时间运行时,其破坏力是 惊人的,从性能下降到内存耗尽,甚至会影响到其他程序的正常运行。另外内存问题的一个共同特点是,内存问题本身并不会有很明显的现象,当有异常现象出现时 已时过境迁,其现场已非出现问题时的现场了,这给调试内存问题带来了很大的难度。
Visual Leak Detector是一款用于Visual C++的免费的内存泄露检测工具。可以在 下载到。相比较其它的内存泄露检测工具,它在检测到内存泄漏的同时,还具有如下特点:
可以得到内存泄漏点的调用堆栈,如果可以的话,还可以得到其所在文件及行号;
可以得到泄露内存的完整数据;
可以设置内存泄露报告的级别;
它是一个已经打包的lib,使用时无须编译它的源代码。而对于使用者自己的代码,也只需要做很小的改动;
他的源代码使用GNU许可发布,并有详尽的文档及注释。对于想深入了解堆内存管理的读者,是一个不错的选择。
可见,从使用角度来讲,Visual Leak Detector简单易用,对于使用者自己的代码,唯一的修改是#include Visual Leak Detector的头文件后正常运行自己的程序,就可以发现内存问题。从研究的角度来讲,如果深入Visual Leak Detector源代码,可以学习到堆内存分配与释放的原理、内存泄漏检测的原理及内存操作的常用技巧等。
本文首先将介绍Visual Leak Detector的使用方法与步骤,然后再和读者一起初步的研究Visual Leak Detector的源代码,去了解Visual Leak Detector的工作原理。
使用 Visual Leak Detector(1.0)
下面让我们来介绍如何使用这个小巧的工具。
首先从网站上下载zip包,解压之后得到vld.h, vldapi.h, vld.lib, vldmt.lib, vldmtdll.lib, dbghelp.dll等文件。将.h文件拷贝到Visual C++的默认include目录下,将.lib文件拷贝到Visual C++的默认lib目录下,便安装完成了。因为版本问题,如果使用windows 2000或者以前的版本,需要将dbghelp.dll拷贝到你的程序的运行目录下,或其他可以引用到的目录。
注:我下载的是较新版1.9,直接安装到系统中。因此使用时必须先在VC中设置一下目录。
接下来需要将其加入到自己的代码中。方法很简单,只要在包含入口函数的.cpp文件中包含vld.h就可以。如果这个cpp文件包含了stdafx.h,则将包含vld.h的语句放在stdafx.h的包含语句之后,否则放在最前面。如下是一个示例程序:
#include &vld.h&
void main()
<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b
#include &vld.h&&void main()&{&…&&&&&&&&&&&&&&&&&}
接下来让我们来演示如何使用Visual Leak Detector检测内存泄漏。下面是一个简单的程序,用new分配了一个int大小的堆内存,并没有释放。其申请的内存地址用printf输出到屏幕上。
编译运行后,在标准输出窗口得到:
p=003a89c0
<div class="crayon-num" data-line="crayon-5ab82b
p=003a89c0
在Visual C++的Output窗口得到:
WARNING: Visual Leak Detector detected memory leaks!
&#8212;&#8212;&#8212;- Block 57 at 0x003A89C0: 4 bytes &#8212;&#8212;&#8212;-
&#8211;57号块0x003A89C0地址泄漏了4个字节
Call Stack:
&#8212;下面是调用堆栈
d:/test/testvldconsole/testvldconsole/main.cpp (7): f
&#8212;表示在main.cpp第7行的f()函数
d:/test/testvldconsole/testvldconsole/main.cpp (14): main –双击以引导至对应代码处
f:/rtm/vctools/crt_bld/self_x86/crt/src/crtexe.c (586): __tmainCRTStartup
f:/rtm/vctools/crt_bld/self_x86/crt/src/crtexe.c (403): mainCRTStartup
0x7C816D4F (File and line number not available): RegisterWaitForInputIdle
&#8212;这是泄漏内存的内容,0x
78 56 34 12
xV4&#8230;.. &#8230;&#8230;..
Visual Leak Detector detected 1 memory leak.
第二行表示57号块有4字节的内存泄漏,地址为0x003A89C0,根据程序控制台的输出,可以知道,该地址为指针p。程序的第7行,f()函数里,在该地址处分配了4字节的堆内存空间,并赋值为0x,这样在报告中,我们看到了这4字节同样的内容。
可以看出,对于每一个内存泄漏,这个报告列出了它的泄漏点、长度、分配该内存时的调用堆栈、和泄露内存的内容(分别以16进制和文本格式列出)。双击该堆栈报告的某一行,会自动在代码编辑器中跳到其所指文件的对应行。这些信息对于我们查找内存泄露将有很大的帮助。
这是一个很方便易用的工具,安装后每次使用时,仅仅需要将它头文件包含进来重新build就可以。而且,该工具仅在build Debug版的时候会连接到你的程序中,如果build Release版,该工具不会对你的程序产生任何性能等方面影响。所以尽可以将其头文件一直包含在你的源代码中。
Visual Leak Detector 工作原理
下面让我们来看一下该工具的工作原理。
在这之前,我们先来看一下Visual C++内置的内存泄漏检测工具是如何工作的。Visual C++内置的工具CRT Debug Heap工作原来很简单。在使用Debug版的malloc分配内存时,malloc会在内存块的头中记录分配该内存的文件名及行号。当程序退出时CRT会在main()函数返回之后做一些清理工作,这个时候来检查调试堆内存,如果仍然有内存没有被释放,则一定是存在内存泄漏。从这些没有被释放的内存块的头中,就可以获得文件名及行号。
这种静态的方法可以检测出内存泄漏及其泄漏点的文件名和行号,但是并不知道泄漏究竟是如何发生的,并不知道该内存分配语句是如何被执行到的。要想了解这些,就必须要对程序的内存分配过程进行动态跟踪。Visual Leak Detector就是这样做的。它在每次内存分配时将其上下文记录下来,当程序退出时,对于检测到的内存泄漏,查找其记录下来的上下文信息,并将其转换成报告输出。
Visual Leak Detector要记录每一次的内存分配,而它是如何监视内存分配的呢?Windows提供了分配钩子(allocation hooks)来监视调试堆内存的分配。它是一个用户定义的回调函数,在每次从调试堆分配内存之前被调用。在初始化时,Visual Leak Detector使用_CrtSetAllocHook注册这个钩子函数,这样就可以监视从此之后所有的堆内存分配了。
如何保证在Visual Leak Detector初始化之前没有堆内存分配呢?全局变量是在程序启动时就初始化的,如果将Visual Leak Detector作为一个全局变量,就可以随程序一起启动。但是C/C++并没有约定全局变量之间的初始化顺序,如果其它全局变量的构造函数中有堆内存分配,则可能无法检测到。Visual Leak Detector使用了C/C++提供的#pragma init_seg来在某种程度上减少其它全局变量在其之前初始化的概率。根据#pragma init_seg的定义,全局变量的初始化分三个阶段:首先是compiler段,一般c语言的运行时库在这个时候初始化;然后是lib段,一般用于第三方的类库的初始化等;最后是user段,大部分的初始化都在这个阶段进行。Visual Leak Detector将其初始化设置在compiler段,从而使得它在绝大多数全局变量和几乎所有的用户定义的全局变量之前初始化。
记录内存分配
一个分配钩子函数需要具有如下的形式:
int YourAllocHook( int allocType, void *userData, size_t size,
int blockType, long requestNumber, const unsigned char*filename, int lineNumber);
<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b
int YourAllocHook( int allocType, void *userData, size_t size, int blockType, long requestNumber, const unsigned char*filename, int lineNumber);
就像前面说的,它在Visual Leak Detector初始化时被注册,每次从调试堆分配内存之前被调用。这个函数需要处理的事情是记录下此时的调用堆栈和此次堆内存分配的唯一标识——requestNumber。
得到当前的堆栈的二进制表示并不是一件很复杂的事情,但是因为不同体系结构、不同编译器、不同的函数调用约定所产生的堆栈内容略有不同,要解释堆栈并得到整个函数调用过程略显复杂。不过windows提供一个StackWalk64函数,可以获得堆栈的内容。StackWalk64的声明如下:
BOOL StackWalk64(
DWORD MachineType,
HANDLE hProcess,
HANDLE hThread,
LPSTACKFRAME64 StackFrame,
PVOID ContextRecord,
PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine,
PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine,
PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine,
PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress
1234567891011
BOOL StackWalk64(&&DWORD MachineType,&&HANDLE hProcess,&&HANDLE hThread,&&LPSTACKFRAME64 StackFrame,&&PVOID ContextRecord,&&PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine,&&PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine,&&PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine,&&PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress);
STACKFRAME64结构表示了堆栈中的一个frame。给出初始的STACKFRAME64,反复调用该函数,便可以得到内存分配点的调用堆栈了。
// Walk the stack.
while (count & _VLD_maxtraceframes) {
if (!pStackWalk64(architecture, m_process, m_thread, &frame, &context,
NULL, pSymFunctionTableAccess64, pSymGetModuleBase64, NULL)) {
// Couldn't trace back through any more frames.
if (frame.AddrFrame.Offset == 0) {
// End of stack.
// Push this frame's program counter onto the provided CallStack.
callstack-&push_back((DWORD_PTR)frame.AddrPC.Offset);
<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b
// Walk the stack.&&&&&while (count & _VLD_maxtraceframes) {&&&&&&&&&count++;&&&&&&&&&if (!pStackWalk64(architecture, m_process, m_thread, &frame, &context,&&&&&&&&&&&&&&&&&&&&&&&&&&&NULL, pSymFunctionTableAccess64, pSymGetModuleBase64, NULL)) {&&&&&&&&&&&&&// Couldn't trace back through any more frames.&&&&&&&&&&&&&break;&&&&&&&&&}&&&&&&&&&if (frame.AddrFrame.Offset == 0) {&&&&&&&&&&&&&// End of stack.&&&&&&&&&&&&&break;&&&&&&&&&}& &&&&&&&&&// Push this frame's program counter onto the provided CallStack.&&&&&&&&&callstack-&push_back((DWORD_PTR)frame.AddrPC.Offset);&&&&&}
那么,如何得到初始的STACKFRAME64结构呢?在STACKFRAME64结构中,其他的信息都比较容易获得,而当前的程序计数器(EIP)在x86体系结构中无法通过软件的方法直接读取。Visual Leak Detector使用了一种方法来获得当前的程序计数器。首先,它调用一个函数,则这个函数的返回地址就是当前的程序计数器,而函数的返回地址可以很容易的从堆栈中拿到。下面是Visual Leak Detector获得当前程序计数器的程序:
#if defined(_M_IX86) || defined(_M_X64)
#pragma auto_inline(off)
DWORD_PTR VisualLeakDetector::getprogramcounterx86x64 ()
__asm mov AXREG, [BPREG + SIZEOFPTR] // Get the return address out of the current stack frame
__asm mov [programcounter], AXREG
// Put the return address into the variable we'll return
#pragma auto_inline(on)
#endif // defined(_M_IX86) || defined(_M_X64)
<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b
#if defined(_M_IX86) || defined(_M_X64)&#pragma auto_inline(off)&DWORD_PTR VisualLeakDetector::getprogramcounterx86x64 ()&{&&&&&DWORD_PTR programcounter;& &&&&&__asm mov AXREG, [BPREG + SIZEOFPTR] // Get the return address out of the current stack frame&&&&&__asm mov [programcounter], AXREG&&&&// Put the return address into the variable we'll return& &&&&&return programcounter;&}&#pragma auto_inline(on)&#endif // defined(_M_IX86) || defined(_M_X64)
得到了调用堆栈,自然要记录下来。Visual Leak Detector使用一个类似map的数据结构来记录该信息。这样可以方便的从requestNumber查找到其调用堆栈。分配钩子函数的allocType参数表示此次堆内存分配的类型,包括_HOOK_ALLOC, _HOOK_REALLOC, 和 _HOOK_FREE,下面代码是Visual Leak Detector对各种情况的处理。
switch (type) {
case _HOOK_ALLOC:
visualleakdetector.hookmalloc(request);
case _HOOK_FREE:
visualleakdetector.hookfree(pdata);
case _HOOK_REALLOC:
visualleakdetector.hookrealloc(pdata, request);
visualleakdetector.report("WARNING: Visual Leak Detector: in allochook(): Unhandled allocation type (%d)./n", type);
<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b
switch (type) {&&&&&case _HOOK_ALLOC:&&&&&&&&&visualleakdetector.hookmalloc(request);&&&&&&&&&break;& &&&&&case _HOOK_FREE:&&&&&&&&&visualleakdetector.hookfree(pdata);&&&&&&&&&break;& &&&&&case _HOOK_REALLOC:&&&&&&&&&visualleakdetector.hookrealloc(pdata, request);&&&&&&&&&break;& &&&&&default:&&&&&&&&&visualleakdetector.report("WARNING: Visual Leak Detector: in allochook(): Unhandled allocation type (%d)./n", type);&&&&&&&&&break;&&&&&}
这里,hookmalloc()函数得到当前堆栈,并将当前堆栈与requestNumber加入到类似map的数据结构中。hookfree()函数从类似map的数据结构中删除该信息。hookrealloc()函数依次调用了hookfree()和hookmalloc()。
检测内存泄露
前面提到了Visual C++内置的内存泄漏检测工具的工作原理。与该原理相同,因为全局变量以构造的相反顺序析构,在Visual Leak Detector析构时,几乎所有的其他变量都已经析构,此时如果仍然有未释放之堆内存,则必为内存泄漏。
分配的堆内存是通过一个链表来组织的,检查内存泄漏则是检查此链表。但是windows没有提供方法来访问这个链表。Visual Leak Detector使用了一个小技巧来得到它。首先在堆上申请一块临时内存,则该内存的地址可以转换成指向一个_CrtMemBlockHeader结构,在此结构中就可以获得这个链表。代码如下:
char *pheap =
_CrtMemBlockHeader *pheader = pHdr(pheap)-&pBlockHeaderN
char *pheap = new char;&&&&&_CrtMemBlockHeader *pheader = pHdr(pheap)-&pBlockHeaderNext;&delete pheap;
其中pheader则为链表首指针。
前面讲了Visual Leak Detector如何检测、记录内存泄漏及其其调用堆栈。但是如果要这个信息对程序员有用的话,必须转换成可读的形式。Visual Leak Detector使用SymGetLineFromAddr64()及SymFromAddr()生成可读的报告。
// Iterate through each frame in the call stack.
for (frame = 0; frame & callstack-&size(); frame++) {
// Try to get the source file and line number associated with
// this program counter address.
if (pSymGetLineFromAddr64(m_process,
(*callstack)[frame], &displacement, &sourceinfo)) {
// Try to get the name of the function containing this program
// counter address.
if (pSymFromAddr(m_process, (*callstack)[frame],
&displacement64, pfunctioninfo)) {
functionname = pfunctioninfo-&N
functionname = "(Function name unavailable)";
<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b<div class="crayon-num crayon-striped-num" data-line="crayon-5ab82b<div class="crayon-num" data-line="crayon-5ab82b
// Iterate through each frame in the call stack.&&&&&&&&&&&&&for (frame = 0; frame & callstack-&size(); frame++) {&&&&&&&&&&&&&&&&&// Try to get the source file and line number associated with&&&&&&&&&&&&&&&&&// this program counter address.&&&&&&&&&&&&&&&&&if (pSymGetLineFromAddr64(m_process,&&&&&&&&&&&&&&&&&&& (*callstack)[frame], &displacement, &sourceinfo)) {&&&&&&&&&&&&&&&&&&&&&...&&&&&&&&&&&&&&&&&}& &&&&&&&&&&&&&&&&&// Try to get the name of the function containing this program&&&&&&&&&&&&&&&&&// counter address.&&&&&&&&&&&&&&&&&if (pSymFromAddr(m_process, (*callstack)[frame],&&&&&&&&&&&&&&&&&&&&&&displacement64, pfunctioninfo)) {&&&&&&&&&&&&&&&&&&&&&functionname = pfunctioninfo-&Name;&&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&&&&&else {&&&&&&&&&&&&&&&&&&&&&functionname = "(Function name unavailable)";&&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&&&&&...&&&&&&&&&&&&&}
概括讲来,Visual Leak Detector的工作分为3步,首先在初始化注册一个钩子函数;然后在内存分配时该钩子函数被调用以记录下当时的现场;最后检查堆内存分配链表以确定是否存在内存泄漏并将泄漏内存的现场转换成可读的形式输出。有兴趣的读者可以阅读Visual Leak Detector的源代码。
在使用上,Visual Leak Detector简单方便,结果报告一目了然。在原理上,Visual Leak Detector针 对内存泄漏问题的特点,可谓对症下药——内存泄漏不是不容易发现吗?那就每次内存分配是都给记录下来,程序退出时算总账;内存泄漏现象出现时不是已时过境 迁,并非当时泄漏点的现场了吗?那就把现场也记录下来,清清楚楚的告诉使用者那块泄漏的内存就是在如何一个调用过程中泄漏掉的。
Visual Leak Detector是一个简单易用内存泄漏检测工具。现在最新的版本是1.9a,采用了新的检测机制,并在功能上有了很多改进。读者不妨体验一下
可能感兴趣的话题
最新版本是2.5啦
关于伯乐在线博客
在这个信息爆炸的时代,人们已然被大量、快速并且简短的信息所包围。然而,我们相信:过多“快餐”式的阅读只会令人“虚胖”,缺乏实质的内涵。伯乐在线内容团队正试图以我们微薄的力量,把优秀的原创文章和译文分享给读者,为“快餐”添加一些“营养”元素。
新浪微博:
推荐微信号
(加好友请注明来意)
&#8211; 好的话题、有启发的回复、值得信赖的圈子
&#8211; 分享和发现有价值的内容与观点
&#8211; 为IT单身男女服务的征婚传播平台
&#8211; 优秀的工具资源导航
&#8211; 翻译传播优秀的外文文章
&#8211; 国内外的精选文章
&#8211; UI,网页,交互和用户体验
&#8211; 专注iOS技术分享
&#8211; 专注Android技术分享
&#8211; JavaScript, HTML5, CSS
&#8211; 专注Java技术分享
&#8211; 专注Python技术分享
& 2018 伯乐在线}

我要回帖

更多关于 链表空间释放 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信