HEAP: Free Heap block 39rt98 modified at 39rtc0 after it was freed
一. 介绍这个问题之前,先来简要对一些概念进行梳理。
Heap,堆,一种由程序员根据实际需求人为分配、释放的数据存储单元。
通常情况下,通过调用malloc函数,或者 new特殊字符来创建出来。
相对应的是通过调用free函数,或者delete操作符进行人为释放。
与之相对应的是Stack,栈,一种由操作系统控制分配、使用、释放的数据存储单元。
二. HEAP: Free Heap block 39rt98 modified at 39rtc0 after it was freed
这句话的意思是,释放了内存开始为0x39rt98的堆块后,堆块所在内存的0x39rtc0位置被更改了。
这样就好办了,我们只需要查找到0x39rt9所在位置属于分配给了哪个对象,然后再定位0x39rtc0所处的成员变量
然后查找所有使用该变量并进行了变量内容的修改的代码就可以定位到错误位置了。再根据情况修改代码,避免内存释放后仍然进行修改即可。
三.出现该问题一般就是使用了野指针导致的.
那么何为野指针呢?
野指针与NULL指针不同.所谓野指针是指指针所指的内存已经回收,而继续使用该指针,导致未定义行为.
内存已经被释放了,后面还继续使用。简单点说就是野指针!而造成野指针的原因很简单,就是内存被释放了。
于是,问题变简单了:找出所有释放内存的地方。释放内存的,主要就2个:free和delete.
于是,我定义了3个宏,
SAFE_DEL_PTR(p) if(p){delete (p); (p)=NULL;}
SAFE_DEL_ARRAY(p) if(p){delete [] (p); (p)=NULL;}
SAFE_FREE_PTR(p) if(p){free(p); p=NULL;}
然后把所有用到这两个的地方都用宏替换掉,然后再把这两个宏里面真正释放的给注释掉。再跑,发现很OK,不会崩溃了。 于是,确认了是释放引起的。
四.也有用gflags来处理这类堆释放问题的。
用windows的debug tools中的global flags即gflags就可以了。gflags工作的时候,可以在用户每次分配内存的时候都在要分配的内存后边紧跟着分配一个保护区间(一般是一个虚拟页),一旦出现堆越界,就会立刻触发中断,这样就可以让调试器准确的定位代码行。
这里所说的立刻触发中断并不是必然的,取决于gflags的设置参数,如果启用了/unaligned参数,则会以不对齐的方式分配保护区间,这样即使越界一个字节都会立即触发异常;默认情况下分配保护区间是以对齐内存为单位的,因此在下面这种越界情况是检测不到的:
int main()
{
char *p = new char[10];
for(int i=10; i<16; i++)
p[i] = i;
return 0;
}
具体的gflags使用可以参见使用手册,最常用的几个指令如下:
>> gflags.exe /p /enable myapp.exe /full #对进程myapp.exe完全启用page heap,注意进程名不能带路径
>> gflags.exe /p /disable myapp.exe #禁止myapp的page heap
>> gflags.exe /p /enable myapp.exe /full /unaligned #非对齐方式启用page heap
另外值得注意的是:由于gflags会给程序分配大量的保护区间,因此调试的时候会占用大量的内存,一定要保证机器设置了足够的虚拟内存空间,并且在不进行调试的时候及时禁用页堆。
操作步骤如下:
(1).安装好之后,把gflags所在文件夹设置到环境变量的path中,方便命令行使用。(我的电脑-->右键-->属性-->高级-->环境变量-->系统变量-->path)
(2). HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\test.exe写入了PageHeapFlags=0x23
(3). cmd 命令。
gflags.exe /p /enable myapp.exe /full
gflags.exe /p /disable myapp.exe