缘起
前一阵子,朋友开发的 QT
程序遇到了一个崩溃问题,抓了 dump
。我跟着一起分析的。最后发现是由于内存分配失败导致的。
一起来练习一下排查思路吧。
说明: 本文很早就写好了草稿,一直没整理成文,Finally~
万能的 !analyze -v
拿到转储文件,第一件事就是点击 !analyze -v
超链接,让 windbg
帮忙自动分析。
从输出结果中的 Qt5Core!qBadAlloc+0x1a
可以猜测是内存分配失败导致的问题。
分配了多大内存?
加载好符号文件,输入 k
命令查看调用栈。
显示的调用栈,需要使用 !analyze -v
分析结果中的 .cxr 0x59cba8
切换上下文,然后再执行 k
命令即可查看到有意义的调用栈了。
查看栈帧 03
对应的代码,可以发现是在通过 file->readAll()
读取整个文件的内容。
根据代码逻辑及函数局部变量的值,可以确定要读取的文件大小大概是 288.6MB
。
空闲空间够大吗?
相比于之前分析空闲内存空间是否足够的办法(感兴趣的小伙伴儿可以点击 这里 查看),我们可以使用更高级的命令进行查找。命令如下:
!address -f:Free -c:".if ( %3 >= 0n302630400) {.echo %1 %2 %3}"
执行后可以发现,输出结果是空,如果减小 0n302630400
为 0n102630400
,可以发现有两条记录。说明命令是有效的,而且进程中已经没有一块空闲地址空间可以满足本次分配请求。
更进一步
如果使用 !address -summary
查看地址空间情况,可以发现空闲空间大小是 1.025GB
,占整个内存空间的 51.24%
。说明当前程序是一个 32
位的程序。
使用 !dh client
查看 client
模块的 PE
文件头信息,可以发现确实是 32 位程序,而且没有开启 Large Address Aware
标志。
解决
不要一次性读取全部文件到内存中
这样修改后,可以处理非常大的文件。但是需要修改代码逻辑,相对复杂。
修改程序为
64
位非常简单,只需要重新编译即可。
设置
Large Address Aware
标志用户内存空间可以达到
3GB
,可以在一定程度上避免内存分配失败的问题。
总结
本次排查相对顺利,解决也比较简单,虽然简单,还是有一些收获的。
可以在
windbg
中使用!address -f:Free -c:".if ( %3 >= mem_size) {.echo %1 %2 %3}"
快速查找大于mem_size
的空闲空间。可以在
windbg
中使用!dh
命令查看PE
文件头信息。可以快速确定一些关键信息,比如模块是32
位的还是64
位的,是否开启Large Address Aware
标志等。如果可以尽量使用
64
位程序,如果必须使用32
位程序,至少也要开启Large Address Aware
标志。可以通过工程属性页中的
链接器 -> 系统 -> 启用大地址
进行开启。