导读
JVM是Java Virtual Machine的缩写,中文名为Java虚拟机。它是一种用于计算设施的标准,是一个虚构进去的计算机,次要通过在理论的计算机上仿真模仿各种计算机性能来实现的。在理论使用过程中,易观技术人员留神到一台开发机上各个微服务过程占用内存很高,随即便开展了考察......
景象:前段时间发现某台开发机上各个微服务过程占用内存很高,这里记录下解决思路,仅供参考。
- Centos6.10+Jdk1.8+SpringBoot1.4.4环境下各个JVM过程所占内存应用状况
VIRT和RES都很高......
以其中某个过程为例(过程启动日期为8月9日,排查时候的工夫是8月10日10:54:58,也就是说该过程运行工夫应该不会超过48小时)
top命令查看该过程占用内存状况(能够看到此过程曾经占用2.7G物理内存)
为了排除掉是因为中途有压力测试的嫌疑,将此服务进行了重启,然而刚起的过程(19146),
占内存状况RES:1.8G, VIRT:33.4G …
JVM过程动不动就是2G以上的内存,然而开发环境并没什么业务申请,谁是罪魁祸首 ?
解决问题之前,先温习下几个基础知识。
1. 什么是RES和VIRT?
- RES:resident memory usage 常驻内存
(1)过程以后应用的内存大小,但不包含swap out
(2)蕴含其余过程的共享
(3)如果申请100m的内存,理论应用10m,它只增长10m,与VIRT相同
(4)对于库占用内存的状况,它只统计加载的库文件所占内存大小
RES = CODE + DATA
- VIRT:virtual memory usage
(1)过程“须要的”虚拟内存大小,包含过程应用的库、代码、数据等
(2)如果过程申请100m的内存,但理论只应用了10m,那么它会增长100m,而不是理论的使用量
VIRT = SWAP + RES
2. Linux与过程内存模型
3. JVM内存模型(1.7与1.8之间的区别)
所以JVM过程内存大小大抵为:
非heap(非heap=元空间+栈内存+…)+heap+JVM过程运行所需内存+其余数据
那么会是jvm内存透露引起的吗?
- 应用Jmap命令将整个heap dump下来,而后用jvisualvm剖析
能够看到,堆内存一切正常(dump会引起FGC,但并不影响此论断)
那么可能是SpringBoot的起因吗?
为了验证此问题,通过部署零碎在开发机上起了1个没有任何业务代码的springboot过程,仅仅是引入注册核心
查看此过程内存占用状况:
显著曾经设置了Xmx为512MB,尽管Xmx不等于最终JVM所占总内存,但至多也不会偏差太多; 那么应用jmap命令查看以后jvm堆内存配置和应用状况(上面的图2是在图1现场5分钟之后截取的)
**(图1)
**
(图2)
所以从2次的jmap后果中,能够得出以下几个论断:
- 咱们的Xmx设置并没有失效,因为MaxHeapSize≠Xmx
- 图1中jvm占用内存计算:
元空间(20.79MB)+ eden(834MB)+年轻代(21MB)+线程栈(38*1024KB)+JVM过程自身运行内存+ NIO的DirectBuffer +JIT+JNI+…≈top(Res) 1.1G
以后jvm线程数统计:jstack 7311 |grep ‘tid’|wc –l (linux 64位零碎中jvm线程默认栈大小为1MB)
- Eden区进行了屡次扩容,由图1可知eden区可用空间曾经不够用了(容量:843MB,已应用:834MB),图2中扩容到1566MB
- Eden区经验了Minor Gc,由图2可知eden区已应用空间:60MB,阐明之前在eden区的对象大部分曾经被回收,局部未被回收的对象曾经转入到扩大1区了
Xmx设置为何未失效?
查看部署零碎的启动脚本,发现启动形式为:Java –jar $jar_file –Xms512m –Xmx1024m
正确的Java命令:
java [ options ] class [ arguments ]
java [ options ] -jar file.jar [ arguments ]
其实到这里,也找到了此问题起因所在,Java –jar $jar_file –Xms512m –Xmx1024m被JVM解释成了程序的参数。
手动执行: java –Xms512m –Xmx1024m –jar ems-client-1.0.jar
至此,RES过高的问题已解决,然而VIRT的问题还在
应用系统命令pmap -x 3516查看过程的内存映射状况,会发现大量的64MB内存块存在;统计了下,大略有50多个65404+132=65536,正好是64MB,算起来大概3个多G
于是Google之,发现大抵的起因是从glibc2.11版本开始,linux为了解决多线程下内存调配竞争而引起的性能问题,加强了动态内存调配行为,应用了一种叫做arena的memory pool,在64位零碎上面缺省配置是一个arena大小为64M,一个过程能够最多有cpu cores 8个arena。假如机器是8核的,那么最多能够有8 8 = 64个arena,也就是会应用64 * 64 = 4096M内存。
然而咱们能够通过设置零碎环境变量来扭转arena的数量:
export MALLOC_ARENA_MAX=8(个别倡议配置程序cpu核数)
配置环境变量使其失效,再重启该jvm过程,VIRT比之前少了快2个G:
/ 具体的参考资料 /
https://access.redhat.com/doc...
https://code.woboq.org/usersp...
总结:这里只是提供一种解决问题的思路,仅供参考;个别咱们遇到问题之后, 首先想到的是不是程序有问题,而后跟踪了很久还是未找到问题根本原因;几经周折, 才发现问题是呈现在最容易被咱们漠视的中央(比方这里的脚本命令问题)!当然,每个公司或者每个人遇到的问题都不太一样,须要具体问题具体分析。