JVM内存用量的再学习

发布时间 2023-12-05 09:29:32作者: 济南小老虎

JVM内存用量的再学习


背景

最近解决一个SQLServer的问题耗时很久. 
最终找到了一个看似合理的问题解释. 
但是感觉不能只是总结于数据库方面 
因为为了解决这个问题增加了很多监控措施. 
所以想就这这个问题, 总结一下这次问题诊断过程中学习到的JVM相关知识. 

一个JVM的监控图表

堆区信息

image

类加载

image

栈信息

image


内存学习

启动脚本为:

-javaagent:./jmx_prometheus_javaagent-0.17.2.jar=8080:simple-config.yml -XX:+PrintSafepointStatistics -XX:PrintSafepointStatisticsCount=1 -XX:+SafepointTimeout   -XX:SafepointTimeoutDelay=2000  -XX:+UseCountedLoopSafepoints  -XX:-UseBiasedLocking -XX:+UnlockDiagnosticVMOptions -XX:GuaranteedSafepointInterval=0 -XX:+PrintGCApplicationStoppedTime -XX:+PrintGCDetails -XX:-DisableExplicitGC -XX:+PrintGC -XX:+PrintGCDateStamps  -XX:-UseGCOverheadLimit -XX:+PrintGCTimeStamps -Xloggc:./dump/gclog -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./dump -XX:NativeMemoryTracking=detail  -XX:+UnlockDiagnosticVMOptions  -Xmx24g -Xms24g 

关于监控的学习和了解

第一部分: 关于堆区
堆区设置的是24G内存

可以看到因为没有设置 老年代和青年带的比率, 就是按照默认的 2:1 进行分配
老年代 16G内存 青年代8G内存. 
幸存者区域 也是按照青年代的 8:1:1 进行默认分配,也就是只有青年代的 10%的代傲, 最大值是 805MB. 

内存的使用之, 基本上也达到了 16+8 =24G的堆区最大值. 

第二部分: 关于非堆区
1. 元数据区占用了 1.87GB, 接近 2GB的内存用量. 
2. 压缩元类区域: 200MB左右. 
3. Code cache 占用了 基本上默认的最大 240MB

监控到的费堆区合计使用了 2.5GB左右. 

通过acuator 或者是 jvm exporter 监控信息获得: 
两者合计为: 26.5GB

另外线程数数量较多, 最大值有 800个线程数 理论上Linux下面每一个线程至少要1MB的内存
所以这一块栈区的内存用量也不小, 也有接近1GB的区域. 

第三部分: 
同时进入系统查看 top 或者是 ps命令查看内存信息
1. top 后输入 大写的M 查看内存使用量较高的进程. 
  PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
32087 root      20   0   34.1g  29.6g 146164 S  27.6  62.7   1507:15 java

RES的内存足有  29.6G 比刚才计算的 26.5G  要多3GB的空间. 

2. 使用 ps -aux |grep java 进行查看
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root     32087  243 62.7 35740952 31017360 ?   Sl   12月04 1507:17 

内存用量其实也是  30G左右. 

简单总结

设置堆区为 24G, 实际上堆区占整个进程的内存用量为:
24/30 = 80%

换句话说, 如果是容器内. 我们这样一个重型应用. 设置内存到32G的pod 的limit值
不建议 堆区的占用量超过80%  设置不建议超过 75% 

因为我ps 和 top 查看时堆区的内存应该已经下降了, 其他区域内存的占比要比
(30-24) 的6GB的使用量还要搞. 
这6GB 不仅仅包含监控中看到的 非堆区还有栈区 以及 GC使用的内存还有一部分直接内存
但是这一块应该是不包含加载到系统里面的缓存数据. 

另外监控中还可以看到. 类的数量其实非常多. 
程序启动完时就已经有了  16万个类, 运行完所有的功能后 类已经有 27.5万个类

计划下次有时间总结一一下 类和对象的关系.