springboot内存占用过高问题排查 - jvm内存使用分析

6 篇文章 0 订阅
1 篇文章 0 订阅

排查springboot内存占用过高问题

所需命令:

ps命令:Linux命令。强大的进程状态监控命令。可以查看进程以及进程中线程的当前CPU使用情况。属于当前状态的采样数据。
top命令:Linux命令。可以查看实时的CPU使用情况。也可以查看最近一段时间的CPU使用情况。
这两个命令详情可参考:https://blog.csdn.net/XiXavier/article/details/108566416

jps命令 :(Java Virtual Machine Process Status Tool)是JDK 1.5提供的一个显示当前所有java进程pid的命令。jps存放在JAVA_HOME/bin/jps,使用时为了方便请将JAVA_HOME/bin/加入到Path.会用 ps 命令也行。

常用的参数:
-q:只显示pid,不显示class名称,jar文件名和传递给main 方法的参数
-m:输出传递给main 方法的参数,在嵌入式jvm上可能是null
-l:输出应用程序main class的完整package名 或者 应用程序的jar文件完整路径名
-v: 输出传递给JVM的参数

示例:
[root@new-frame-251 texu]# jps -lv |grep 32528
32528 hoau-texu-service-0.0.1-SNAPSHOT-exec.jar -Xms80m -Xmx80m -XX:SurvivorRatio=8 -XX:+UseConcMarkSweepGC -XX:+HeapDumpOnOutOfMemoryError
[root@new-frame-251 texu]#

jstack命令:Java提供的命令。可以查看某个进程的当前线程栈运行情况。根据这个命令的输出可以定位某个进程的所有线程的当前运行状态、运行代码,以及是否死锁等等。

[root@new-frame-251 texu]# jstack
Usage:
    jstack [-l] <pid>
        (to connect to running process)
    jstack -F [-m] [-l] <pid>
        (to connect to a hung process)
    jstack [-m] [-l] <executable> <core>
        (to connect to a core file)
    jstack [-m] [-l] [server_id@]<remote server IP or hostname>
        (to connect to a remote debug server)

Options:
    -F  to force a thread dump. Use when jstack <pid> does not respond (process is hung)
    -m  to print both java and native frames (mixed mode)
    -l  long listing. Prints additional information about locks
    -h or -help to print this help message
[root@new-frame-251 texu]#

统计进程中线程个数:(linux 64位系统中jvm线程默认栈大小为1MB)
[root@new-frame-251 texu]# jstack 32528 |grep tid|wc -l
251
[root@new-frame-251 texu]#

查看进程中线程情况:
在这里插入图片描述
GC task thread :垃圾回收线程
http-nio thread :tomcat网络处理网络请求线程
C2CompilerThread :JIT编译线程,动态编译Java运行代码,C2表示编译的是server端代码
DubboServerHandler-10.39.251.159:20914-thread-200 : dubbo线程
还有很多其他的线程。。。

jmap命令: Java提供的命令。查看jvm内存使用情况。

jmap -heap [pid] : 查看整个JVM内存状态,要注意的是在使用CMS GC 情况下,jmap -heap的执行有可能会导致JAVA 进程挂起
jmap -histo [pid] : 查看JVM堆中对象详细占用情况
jmap -histo:live pid : 指定了live子选项,则只计算活动的对象
jmap -dump:format=b,file=文件名 [pid] : 导出整个JVM 中内存信息
......

jvisualvm.exe : java 自带的jvm监控工具,java的安装目录 bin/ 中

pmap命令: - report memory map of a process(查看进程的内存映像信息)pmap命令用于报告进程的内存映射关系

用法
      pmap [ -x | -d ] [-q] pids...
      pmap -V
选项含义
      -x   extended       Show the extended format. 显示扩展格式
      -d   device         Show the device format.   显示设备格式
      -q   quiet          Do not display some header/footer lines. 不显示头尾行
      -V   show version   Displays version of program. 显示版本
扩展格式和设备格式域:
       Address:  start address of map  映像起始地址
       Kbytes:  size of map in kilobytes  映像大小
       RSS:  resident set size in kilobytes  驻留集大小
       Dirty:  dirty pages (both shared and private) in kilobytes  脏页大小
       Mode:  permissions on map 映像权限: r=read, w=write, x=execute, s=shared, p=private (copy on write)  
       Mapping:  file backing the map , or '[ anon ]' for allocated memory, or '[ stack ]' for the program stack.  映像支持文件,[anon]为已分配内存 [stack]为程序堆栈
       Offset:  offset into the file  文件偏移
       Device:  device name (major:minor)  设备名

在这里插入图片描述

jvm各项参数说明

-XX:MetaspaceSize=128m (元空间默认大小)
-XX:MaxMetaspaceSize=128m (元空间最大大小)
-Xms1024m (堆默认大小)此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存。
-Xmx1024m (堆最大大小)Java Heap最大值,默认值为物理内存的1/4,最佳设值应该视物理内存大小及计算机内其他内存开销而定
-Xmn256m (新生代大小)Java Heap Young区,堆大小=年轻代大小 + 年老代大小 + 持久代大小。持久代一般固定大小为64m,所以增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。
-Xss256k (棧最大深度大小)每个线程的Stack大小,JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K。更具应用的线程所需内存大小进行调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。
-XX:SurvivorRatio=8 (新生代分区比例 8:2)
-XX:+UseConcMarkSweepGC (指定使用的垃圾收集器,这里使用CMS收集器)
-XX:+PrintGCDetails (打印详细的GC日志)


JDK8之后把-XX:PermSize 和 -XX:MaxPermGen移除了,取而代之的是
-XX:MetaspaceSize=128m (元空间默认大小)
-XX:MaxMetaspaceSize=128m (元空间最大大小)
JDK 8开始把类的元数据放到本地化的堆内存(native heap)中,这一块区域就叫Metaspace,中文名叫元空间。
使用本地化的内存有什么好处呢?最直接的表现就是java.lang.OutOfMemoryError: PermGen 空间问题将不复存在,因为默认的类的元数据分配只受本地内存大小的限制,也就是说本地内存剩余多少,理论上Metaspace就可以有多大,这解决了空间不足的问题。不过,让Metaspace变得无限大显然是不现实的,因此我们也要限制Metaspace的大小:使用-XX:MaxMetaspaceSize参数来指定Metaspace区域的大小。JVM默认在运行时根据需要动态地设置MaxMetaspaceSize的大小。

排查步骤:

1. 通过 ps -aux |grep pid 或者 top -p pid 命令查看项目内存占用情况(我这里是已经优化完的)
在这里插入图片描述
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. 可以使用 jmap -dump:format=b,file=文件名 [pid] 导出整个JVM 中内存信息,再使用 jvisualvm 工具进行分析。或者使用 jmap -heap [pid] : 查看整个JVM内存状态。内存使用情况如下:

[root@new-frame-251 texu]# jmap -heap 5847
Attaching to process ID 5847, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.211-b12

using parallel threads in the new generation.
using thread-local object allocation.
Concurrent Mark-Sweep GC        ##垃圾回收器

Heap Configuration:
   MinHeapFreeRatio         = 40
   MaxHeapFreeRatio         = 70
   MaxHeapSize              = 83886080 (80.0MB)       ##当前jvm最大堆大小
   NewSize                  = 27918336 (26.625MB)
   MaxNewSize               = 27918336 (26.625MB)
   OldSize                  = 55967744 (53.375MB)
   NewRatio                 = 2
   SurvivorRatio            = 8
   MetaspaceSize            = 21807104 (20.796875MB)        ##元空间大小
   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   MaxMetaspaceSize         = 17592186044415 MB        ## 最大元空间大小
   G1HeapRegionSize         = 0 (0.0MB)

Heap Usage:       ## 当前堆的各个区域使用情况
New Generation (Eden + 1 Survivor Space):
   capacity = 25165824 (24.0MB)
   used     = 5727184 (5.4618682861328125MB)
   free     = 19438640 (18.538131713867188MB)
   22.757784525553387% used
Eden Space:
   capacity = 22413312 (21.375MB)
   used     = 4922248 (4.694221496582031MB)
   free     = 17491064 (16.68077850341797MB)
   21.961270159448098% used
From Space:
   capacity = 2752512 (2.625MB)
   used     = 804936 (0.7676467895507812MB)
   free     = 1947576 (1.8573532104492188MB)
   29.243687220982142% used
To Space:
   capacity = 2752512 (2.625MB)
   used     = 0 (0.0MB)
   free     = 2752512 (2.625MB)
   0.0% used
concurrent mark-sweep generation:
   capacity = 55967744 (53.375MB)
   used     = 48057048 (45.830772399902344MB)
   free     = 7910696 (7.544227600097656MB)
   85.86561573752195% used

28885 interned Strings occupying 3461280 bytes.
[root@new-frame-251 texu]# jstack 5847 |grep tid|wc -l      ## 统计线程数,每个线程都有一个 tid
251
[root@new-frame-251 texu]# jstack 5847 |grep java.lang.Thread.State|wc -l      ##有几个特殊的线程没有状态
246
[root@new-frame-251 texu]#

[root@master159 ~]# jstack 107435 |grep tid |wc -l
259
[root@master159 ~]# jstack 107435|grep 'dubbo-remoting-server-heartbeat' |wc -l
1
[root@master159 ~]# jstack 107435|grep 'DubboServerHandler-10.39.251.159:' |wc -l      ##dubbo 线程有点多可以优化
200
[root@master159 ~]# jstack 107435|grep 'http-nio-10065-' |wc -l
14
[root@master159 ~]#

内存使用计算:(Eden )21.375+(扩展区1)2.625+(扩展区2)2.625+(垃圾回收器)53.375+(线程栈)251*0.25=142.8M
再加上 JVM进程本身运行内存+ NIO的DirectBuffer +JIT+JNI+…≈281M (最开始查看的项目占用内存,设置了-Xss256k,所以一个线程占用256k内存),计算出来很正常。

3. 查看进程中各个线程情况:
top -H -p 5847 中的 pid 的16进制转小写 对应的就是 jstack -l 5847 中的 nid 。使用这两个命令就可以查看线程的内存使用,及具体代码情况
4.

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
4. 前面只是分析了RES 并没有分析 VIRT(虚拟内存),VIRT 分析如下:

使用 pmap -x 5847 查看进程的内存映像信息
在这里插入图片描述
这些内存块加起来就是虚拟内存占用的内存情况;

百度:linux为了解决多线程下内存分配竞争而引起的性能问题,增强了动态内存分配行为,使用了一种叫做arena的memory pool,在64位系统下面缺省配置是一个arena大小为64M,一个进程可以最多有cpu cores * 8个arena。假设机器是8核的,那么最多可以有8 * 8 = 64个arena,也就是会使用64 * 64 = 4096M内存。

可以通过设置系统环境变量来改变arena的数量:
MALLOC_ARENA_MAX=8(一般建议配置程序cpu核数)

现代操作系统里面分配虚拟地址空间操作不同于分配物理内存。在64位操作系统上,可用的最大虚拟地址空间有16EB,即大概180亿GB。那么在一台只有16G的物理内存的机器上,我也能要求获得4TB的地址空间以备将来使用

1.VIRT高是因为分配了太多地址空间导致。
2.一般来说不用太在意VIRT太高,因为你有16EB的空间可以使用。
3.如果你实在需要控制VIRT的使用,设置环境变量MALLOC_ARENA_MAX,例如hadoop推荐值为4,因为YARN使用VIRT值监控资源使用。参考:https://www.sohu.com/a/250985880_756465

结束:

以上只是对jvm内存占用情况进行分析,分析完之后,对异常的地方进行优化即可;

参考:
https://blog.csdn.net/zengwende/article/details/103665545
https://www.jianshu.com/p/8d5782bc596e

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值