面试问题:你工作中遇到的内存问题都是怎么解决的,使用什么工具,适用场景
面试问题:你工作中遇到的内存问题都是怎么解决的,使用什么工具,适用场景
背景
肯定是出现了问题,主要的就是定位问题的所在
1. addr2line (用来查找访问保护地址NULL等)
作用:将指定地址转换为对应的文件名和行号
1. 题外:当我们公司项目编写程序的时候,一般不让使用 abort函数来强制终止程序,我们都会 采取 **NULL**的值作为异常值返回当调用者使用、访问了这个异常的NULL的时候,就会发生错误,就方便我们定位了问题。如果置之不理,可能最终出现问题的地方和实际问题地方查了好远
2. 例子:
#include <stdio.h>
int main() {
int* p = NULL;
*p = (int)"main.c";
return 0;
}
编译、运行 gcc main.c
输出:core dump
当一个大型的程序的时候,怎么搞?
就可以利用addr2line工具
-
步骤:
-
对于 编译的时候需要加上 -g,表示输出有调试信息程序
gcc -g main.c
-
开启core dump,默认是不开启的
ulimit -c unlimited
-
运行程序,并生成崩溃的core文件
./a.out
-
读取core文件,获得IP寄存器
demsg core
得到异常发生的时候的地址
700.663159] a.out[2315]: segfault at 0 ip 080483a9 sp bfc1c178 error 6 in a.out[8048000+1000] [ 792.141021] a.out[2334]: segfault at 0 ip 080483a9 sp bfe9c3e8 error 6 in a.out[8048000+1000]
-
使用addr2line + 地址定位问题发生处
addr2line 0x080483a9 -f -e ./a.out
-
经过上面的步骤,输出就是
main
/home/delphi/test/main.c:6
一下子就可以找到了
2. gdb + core (检查段错误)
步骤:
-
对于 编译的时候需要加上 -g,表示输出有调试信息程序
gcc -g main.c
-
开启core dump,默认是不开启的
ulimit -c unlimited
-
运行程序,并生成崩溃的core文件
./a.out
-
gdb运行程序 + core
gdb ./a.out core
得到异常发生的时候的地址
9 in main () at main.c:6
6 *p = (int)"123";
3. valgrind(检查内存泄漏、重复释放)
作用:对可执行程序进行 内存泄漏检查
使用方法:
valgrind --tool=memcheck --leak-check=yes 可执行程序名称
当有错误就会出现下面的提示
==2545== HEAP SUMMARY:
==2545== in use at exit: 4 bytes in 1 blocks
==2545== total heap usage: 1 allocs, 0 frees, 4 bytes allocated
==2545==
==2545== 4 bytes in 1 blocks are definitely lost in loss record 1 of 1
==2545== at 0x4025BD3: malloc (vg_replace_malloc.c:236)
==2545== by 0x80483D8: main (main.c:5)
==2545==
==2545== LEAK SUMMARY:
==2545== definitely lost: 4 bytes in 1 blocks
==2545== indirectly lost: 0 bytes in 0 blocks
==2545== possibly lost: 0 bytes in 0 blocks
==2545== still reachable: 0 bytes in 0 blocks
==2545== suppressed: 0 bytes in 0 blocks
4. cppcheck (静态解析程序中有问题的地方 空地址访问、内存泄漏)
一般直接使用 cppcheck xx文件,可以用的有*.cpp, *.cxx, *.cc, *.c++ and *.c files,或者直接给 文件夹
参数有:
-
--enable=id Enable additional checks. The available ids are:
all - enable all checks
exceptNew - exception safety when using newexceptRealloc - exception safety when reallocating
style - Check coding style
unusedFunctions - check for unused functions -
--file-list=file Specify the files to check in a text file. One Filename per line
-
-I [dir] Give include path. Give several -I parameters to give
several paths. First given path is checked first. If
paths are relative to source files, this is not needed
5. 进行 系统调用 分析
strace常用来跟踪进程执行时的系统调用和所接收的信号。 在Linux世界,进程不能直接访问硬件设备,当进程需要访问硬件设备(比如读取磁盘文件,接收网络数据等等)时,必须由用户态模式切换至内核态模式,通过系统调用访问硬件设备。strace可以跟踪到一个进程产生的系统调用,包括参数,返回值,执行消耗的时间。
-
输出参数的含义
每一行都是一条系统调用,等号左边是系统调用的函数名及其参数,右边是该调用的返回值。 strace 显示这些调用的参数并返回符号形式的值。strace 从内核接收信息,而且不需要以任何特殊的方式来构建内核
- 跟踪可执行程序
strace -f -F -o ~/straceout.txt myserver
-f -F选项告诉strace同时跟踪fork和vfork出来的进程,-o选项把所有strace输出写到~/straceout.txt里 面,myserver是要启动和调试的程序
- 跟踪服务程序
strace -o output.txt -T -tt -e trace=all -p 28979
跟踪28979进程的所有系统调用(-e trace=all),并统计系统调用的花费时间,以及开始时间(并以可视化的时分秒格式显示),最后将记录结果存在output.txt文件里面
6. 根据进程号得到 堆栈信息
我们有的时候只知道 一个 pid出了问题,但是不清楚 问题点在哪里(死锁?)
可以使用pstack 工具来实现
使用方法:
pstack pid
结果就是
#0 0x00007f50ff291774 in __GI___nanosleep (requested_time=requested_time@entry=0x7ffe89b2ec90, remaining=remaining@entry=0x7ffe89b2ec90) at ../sysdeps/unix/sysv/linux/nanosleep.c:28
#1 0x00007f50ff29167a in __sleep (seconds=0) at ../sysdeps/posix/sleep.c:55
#2 0x000000000040056f in main ()
如果不可用参见pstack无法使用的问题_笔记本专栏-CSDN博客
5. 上面的都是针对已知程序进行分析,那如果不知道呢
1. 第一种推荐 top
系统响应变慢,首先得定位大致的问题出在哪里,是IO瓶颈、CPU瓶颈、内存瓶颈还是程序导致的系统问题
-
终端输入:top
-
进入交互模式后:
输入M,进程列表按内存使用大小降序排序,便于我们观察最大内存使用者使用有问题(检测内存泄漏问题);
输入P,进程列表按CPU使用大小降序排序,便于我们观察最耗CPU资源的使用者是否有问题;
- top第三行显示当前系统的,其中有两个值很关键
%id:空闲CPU时间百分比,如果这个值过低,表明系统CPU存在瓶颈;
%wa:等待I/O的CPU时间百分比,如果这个值过高,表明IO存在瓶颈;
2. free
检查当前可用的内存,
但是这个只能发现内存越来越少,不知道对应的pid是啥
系统实际可用的内存为free工具输出第二行的free+buffer+cached;
可用的内存越来越少也就出了问题