一、定位内存占用高的进程
top指令是最常见的检测系统运行状态的指令,但是因为展示界面和实时刷新的限制,则通过top指令不一定能够发现占用的内存很高的进程。因此,我们使用ps aux指令检索当前系统下的所有运行的进程。
• 下述指令查看系统内存占用前10的进程信息
ps aux|head -n1;ps aux|grep -v PID|sort -nr -k6|head -n10
说明: ps aux|head -n1;表示显示标题行;ps aux|grep -v PID表示结果跳过标题行(含有串PID的行);|sort -nr -k6表示结果按第六列(RSS)从大到小排序;|head -n10表示仅显示前10行数据。
• PID 进程的ID
• USER 进程所有者
• PR 进程的优先级别,越小越优先被执行
• NI nice值
• VIRT 进程占用的虚拟内存
• RES 进程占用的物理内存
• SHR 进程使用的共享内存
• S 进程的状态。S表示休眠,R表示正在运行,Z表示僵死状态,N表示该进程优先值为负数
• %CPU 进程占用CPU的使用率
• %MEM 进程使用的物理内存和总内存的百分比
• TIME+ 该进程启动后占用的总的CPU时间,即占用CPU使用时间的累加值。
• COMMAND 进程启动命令名称
二、查看内存占用的进程内存分配情况
1、pmap pid去检查内存分布情况
pmap 17354
• Address:00378000-0038d000 进程所占的地址空间
• Kbytes 该虚拟段的大小
• RSS 设备号(主设备:次设备)
• Anon 设备的节点号,0表示没有节点与内存相对应
• Locked 是否允许swapped
• Mode 权限:r=read, w=write, x=execute, s=shared, p=private(copy on write)
• Mapping: bash 对应的映像文件名
(1). 部分32位proc节点内存占用高的原因是用户配置了几百个proc处理线程,而1个线程10M,也就意味着光是线程开销,再启动后就占用了1G多了。
(2). 部分业务会把所有几百个业务so都加载到proc处理模块上去,这也会占用几百兆内存。
三、定位具体占用内存高的代码
如果从前面的步骤也没有定位到具体内存占用高的原因,就要结合具体的代码进行排查了。我们使用valgrind工具集的massif工具进行检测。
检测前,用户需要保证自己的测试环境能够重现内存占用高的问题。
massif工具检测时,需要检测的程序和动态库含有符号信息,也就是使用-g属性进行编译集成。一般我们中间件和发布的库都是携带-g属性编译的,使用开发工具生成的业务so也含有-g属性。
massif工具检测时,需要更多的内存消耗和更久的处理耗时,因此推荐在高性能的测试环境上运行。
4.1. 如何使用massif定位内存泄漏问题
在程序启动命令前添加valgrind命令及参数(massif工具内部会记录下程序每次申请与释放内存的地方,并隔一段时间记录下该时刻的内存使用情况),示例如下:蓝色部分为自己启动进程的命令
valgrind --tool=massif --heap=yes --stacks=yes --detailed-freq=1 servert -f dlr_front.xml -start mainsvr -t ar -s 1 -status 0
如果启动的是守护的程序,即测试程序实际上是由守护程序拉起的,我们就要添加--trace-children=yes,示例如下
valgrind --tool=massif --heap=yes --stacks=yes --detailed-freq=1 --trace-children=yes servert -f dlr_front.xml -start monsvr -t ar -s 1 -status 0
程序启动后,按之前的测试流程重复操作,保证测试时间足够长,看到了内存明显增长的现象;
然后停止程序,注意停止时严禁通过kill等指令强杀程序(实际用户强杀的是valgrind程序,导致valgrind无法正常生成检测结果),我们的中间件一般一般要使用servert -stop指令停止;
程序停止后,会生成落地文件massif.out.XXX;
我们可以使用valgrind自带的ms_print查看(非图形数据,不推荐),或者安装massif-visualizer查看(图形界面,推荐)
• 配置项说明
• --heap=<yes|no> 默认yes,表示对堆空间的消耗进行监控
• --stacks=<yes|no> 默认no,表示对栈空间的消耗进行监控,注意多线程时会有多个栈
• --detailed-freq=<n> 默认10,表示详细快照的频率,当=1时表示每个快照都是详细的。
• --trace-children=<yes|no> 默认no,是否监控创建的子进程,默认只会监控创建的主进程,而不关心子进程的内容。
注:含排队机(或守护进程)的场景一般都是要添加--trace-children=yes的,同时由于存在仲裁代理可能强行kill子进程,导致子进程的监控信息无法落地,因此测试结束后,需要先kill仲裁代理的进程,然后通过servert -stop来正常停止排队机。