在项目中遇到配置了
JVM
堆大小之后还是发现真实使用的内存比配置的内存大了很多,接下来就是一列的排查最终解决。
-
排查这个问题需要明白
JVM
的内存使用
-
我们平时只是控制
JVM
堆大小,但是还有一块应用运行临时区可以直接在服务器开辟内存空间 -
所以真实的使用内存大小需要
JVM
空间加上应用运行临时区 -
临时区包含:字节缓冲区、序列化、自定义类型而且经了解没办法
GC
,高手要请教C++自己优化内存空间。 -
搞清楚
JVM
里面的运行机制下一步就要看一下如何定位问题,究竟是哪里的代码出了问题
-
定位问题
-
首先我怀疑是有流没有关掉
-
打印
GC
日志-Xlog:gc*:file=/opt/app/dumps/gc.log
-
观察系统中
GC
后内存变化 -
或者使用
jstat -gcutil pid
或者arthas
等工具查看GC
的回收变化 -
看完之后
GC
正常没有清除不掉的空间
-
-
使用命令定位
# 查看历史 jmap -histo <PID> ## 查看垃圾回收情况 jstat -gcutil <PID> ## 打印内存使用情况 jcmd <PID> VM.native_memory summary scale=MB
jmap
是 Java Memory Map 工具的一部分,用于分析和管理 Java 虚拟机(JVM
)的内存。jmap -histo
命令用于显示 Java 堆内存中的对象实例的直方图。
命令的输出通常包括以下列:
-
Num
: 对象的序号。 -
# Instances
: 对象实例的数量。 -
# Bytes
: 所有实例占用的总字节数。 -
Class Name
: 对象的类名。
例子
yaml复制代码 num #instances #bytes class name ---------------------------------------------- 1: 1000 51200 [B 2: 500 30000 java.lang.String 3: 200 16000 java.util.ArrayList ...
这个输出表示 java.lang.String
类有 500 个实例,占用 30000 字节的内存。
查看内存占用情况没有发现自定义类型
-
导出当前的内存地址
# 内存地址详情 pmap -x <PID> | sort -n -k3 > pmap-sorted.txt
可以看到所有内存中存储的空间,这块要想看具体内存空间中有哪些内容需要自己去找一下;
-
打印堆文件查看对文件的垃圾对象有哪些顽固分子
## 堆转储存 jmap -dump:format=b,file=./dumpfile.hprof 1
最终发现都是一些字节和基础类型。
-
打印当前线程栈信息
# top命令查看当前资源占用情况 top # 紧接着大写的H 可以查看当前所有线程的运行情况 -H ## 可以打印线程栈信息到文件,也可以使用arthas工具 jstack -l 1 > stack.txt
总结:打印出当前所有线程信息发现占用CPU高的线程栈里面的方法,可以定位一般性问题
一顿操作猛如虎,但是我们堆外内存如何控制还是没有找到源头,最终逛论坛很遗憾的发现运行时区域的内存是无法限制的。只能取一个合理值在k8s pod
中给他限制。所以需要把业务节点和计算节点分开部署,保证业务的稳定,计算节点要给一个上线崩溃自启。