当前位置:首页 > 技术分析 > 正文内容

jvm疯狂吃内存,到底是谁的锅?

ruisui884个月前 (01-10)技术分析19

jvm应该是每一个java程序员都需要掌握的内容,但是在没有遇到问题之前,很多都是基于理论的,唯有实战才能增加个人的知识储备。本文是从一个角度来分析是谁在狂吃内存,希望对你有所帮助。本文是易观技术人员注意到一台开发机上各个微服务进程占用内存很高,随即便展开了调查......

ps:本文来源于:https://www.analysys.cn/article/detail/20019016

现象:前段时间发现某台开发机上各个微服务进程占用内存很高,这里记录下解决思路,仅供参考。

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/documentation/enus/red_hat_enterprise_linux/6/html/6.0_release_notes/compiler

https://code.woboq.org/userspace/glibc/malloc/arena.c.html

总结:这里只是提供一种解决问题的思路,仅供参考;一般我们遇到问题之后, 首先想到的是不是程序有问题,然后跟踪了很久还是未找到问题根本原因;几经周折, 才发现问题是出现在最容易被我们忽视的地方(比如这里的脚本命令问题)!当然,每个公司或者每个人遇到的问题都不太一样,需要具体问题具体分析。

本文来源于:https://www.analysys.cn/article/detail/20019016

扫描二维码推送至手机访问。

版权声明:本文由ruisui88发布,如需转载请注明出处。

本文链接:http://www.ruisui88.com/post/136.html

标签: top res
分享给朋友:

“jvm疯狂吃内存,到底是谁的锅?” 的相关文章

gitlab常用命令大全

GitLab常用命令大全GitLab是一个基于Git的Web平台,它不仅提供代码托管,还集成了持续集成/持续交付(CI/CD)、代码审查、问题追踪等功能。在日常使用GitLab的过程中,我们常常需要使用一系列命令来管理代码仓库、处理分支和标签等。以下是GitLab常用的Git命令大全,并附上详细解释...

10分钟搞定gitlab-ci自动化部署

gitlab-ci 是持续集成工具/自动化部署工具,类似 jenkins。持续集成 是将代码集成到共享存储库并尽可能早地自动构建/测试每个更改的实践 - 通常一天几次。概述在编码完成时都会进行打包发布过程,如果每次都手动操作这一步骤就会浪费时间,效率低下。所以就有了持续集成。准备事项请提前安装以下软...

抖音 Android 性能优化系列:启动优化实践

启动性能是 APP 使用体验的门面,启动过程耗时较长很可能使用户削减使用 APP 的兴趣,抖音通过对启动性能做劣化实验也验证了其对于业务指标有显著影响。抖音有数亿的日活,启动耗时几百毫秒的增长就可能带来成千上万用户的留存缩减,因此,启动性能的优化成为了抖音 Android 基础技术团队在体验优化方向...

一起学Vue:路由(vue-router)

前言学习vue-router就要先了解路由是什么?前端路由的实现原理?vue-router如何使用?等等这些问题,就是本篇要探讨的主要问题。vue-router是什么路由是什么?大概有两种说法:从路由的用途上来解释路由就是指随着浏览器地址栏的变化,展示给用户的页面也不相同。从路由的实现原理上来解释路...

从 Vue2.0 到 React17——React 开发入门

作者:佚名来源:前端大全前言找工作时发现有一些公司是以React作为技术栈的,而且薪资待遇都不错,为了增加生存的筹码,所以还是得去学一下React,增加一项求生技能。因为我用Vue2.0开发项目已经四年了,故用Vue2.0开发项目的思路来学习React。前端项目是由一个个页面组成的,对于Vue来说,...

vue打开新窗口并且实现传参,有图有真相

我要实现的功能是打开一个新窗口用来展示新页面,而且需要传参数,并且参数不能显示在地址栏里面,而且当我刷新页面的时候,传过来的参数不能丢失,要一直存在,除非我手动关闭这个新窗口,即浏览器的标签页。通过面向百度编程,发现网上的根本达不到这个效果,而且还都是坑,明明实现不了,还若有其事的写出来,于是我在标...