Java web应用占用内存过高问题排查

现象

1、机器内存不够用,已经达到80以上%

需求

需要知道机器上进程的占用内存情况

查询机器内存进程使用内存情况

查询命令

Linux 查询命令 top 查询正在运行的进程情况。进入监控后按i查询正在运行的进程。找到占内存比高的应用。应用是一个Java 进程内存使用率 35.2%。总内存32779904约等于32G,32g*0.352约等于10.1G
top监控

top - 10:47:41 up 80 days, 20:40,  1 user,  load average: 0.10, 0.08, 0.10
Tasks: 110 total,   1 running, 109 sleeping,   0 stopped,   0 zombie
%Cpu(s):  1.7 us,  0.7 sy,  0.0 ni, 97.6 id,  0.0 wa,  0.0 hi,  0.1 si,  0.0 st
KiB Mem : 82.6/32779904 [|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||                 ]
KiB Swap:  0.0/0        [                                                                                                    ]

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND                                                                                                                                                  
11249 www       20   0   13.4g  11.0g   8048 S   1.0 35.2 718:00.75 java                                                                                                                                                     
 2589 www       20   0 6393604   2.5g   7868 S   2.0  8.1   2458:26 java                                                                                                                                                     
22799 root      20   0 5983600   2.4g   7716 S   4.0  7.6   4880:49 java                                                                                                                                                     
 2585 www       20   0 6213208   2.2g   7808 S   0.7  7.1   1243:40 java                                                                                                                                                     
 2586 www       20   0 4929108   1.4g   7772 S   0.3  4.5   1324:14 java                                                                                                                                                     
 2587 www       20   0 4893244   1.4g   7856 S   1.0  4.5   1234:17 java                                                                                                                                                     
19693 www       20   0 4820408 657000  16676 S   0.3  2.0   3:03.72 java                                                                                                                                                     
 2091 root      20   0 1462680  54860   7424 S   0.3  0.2 389:44.95 icagent                                                                                                                                                  
 2131 root      20   0  705708  11580    680 S   0.7  0.0   2:58.77 icwatchdog                                                                                                                                               
 3085 root      20   0 1421360  11224   6092 S   0.3  0.0 300:09.96 hostguard  

现象

单个应用接口频繁(20W次左右)调用后应用所占内存持续增长。已经增长占用到10G内存。
调用前1G左右 ,20w次调用后 10G左右。

需求

需要单个应用的占用内存详情(线程占用内存,和其他信息),以进一步确定原因。

查询

查询命令
1、Linux线程占用资源情况
命令:top -H -p 进程号(top -H -p 11249)
top -H -p 端口
结果
top - 11:10:29 up 80 days, 21:02,  1 user,  load average: 0.11, 0.10, 0.13
Threads:  90 total,   0 running,  90 sleeping,   0 stopped,   0 zombie
%Cpu(s):  1.8 us,  0.6 sy,  0.0 ni, 97.3 id,  0.1 wa,  0.0 hi,  0.2 si,  0.0 st
KiB Mem : 32779904 total,   238440 free, 26574892 used,  5966572 buff/cache
KiB Swap:        0 total,        0 free,        0 used.  5703160 avail Mem 

  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND                                                                                                                                                   
11417 www       20   0   13.4g  11.0g   8048 S  0.3 35.2  24:48.30 java                                                                                                                                                      
11249 www       20   0   13.4g  11.0g   8048 S  0.0 35.2   0:00.00 java                                                                                                                                                      
11250 www       20   0   13.4g  11.0g   8048 S  0.0 35.2   0:08.13 java                                                                                                                                                      
11251 www       20   0   13.4g  11.0g   8048 S  0.0 35.2  10:58.80 java                                                                                                                                                      
11252 www       20   0   13.4g  11.0g   8048 S  0.0 35.2  10:58.45 java                                                                                                                                                      
11253 www       20   0   13.4g  11.0g   8048 S  0.0 35.2  10:58.34 java                                                                                                                                                      
11254 www       20   0   13.4g  11.0g   8048 S  0.0 35.2  10:59.00 java                                                                                                                                                      
11255 www       20   0   13.4g  11.0g   8048 S  0.0 35.2   3:42.57 java                                                                                                                                                      
11256 www       20   0   13.4g  11.0g   8048 S  0.0 35.2   0:07.67 java                                                                                                                                                      
11257 www       20   0   13.4g  11.0g   8048 S  0.0 35.2   0:12.94 java                                                                                                                                                      
11258 www       20   0   13.4g  11.0g   8048 S  0.0 35.2   0:00.00 java                                                                                                                                                      
11259 www       20   0   13.4g  11.0g   8048 S  0.0 35.2   1:20.63 java                                                                                                                                                      
11360 www       20   0   13.4g  11.0g   8048 S  0.0 35.2  42:55.66 java                                                                                                                                                      
11361 www       20   0   13.4g  11.0g   8048 S  0.0 35.2   0:02.50 java                                                                                                                                                      
11362 www       20   0   13.4g  11.0g   8048 S  0.0 35.2   3:54.86 java                                                                                                                                                      
11363 www       20   0   13.4g  11.0g   8048 S  0.0 35.2   6:54.01 java                                                                                                                                                      
11364 www       20   0   13.4g  11.0g   8048 S  0.0 35.2   0:09.12 java  
.......
分析

现象是 :应用中所有的线程都占用了总的内存大小。
猜测:占用多的部分在,是共享内存。(猜测问题的基础是,linx能够检测到Java进程内部使用资源的情况)

2、java应用占用资源情况(堆栈信息)
命令

jmap -heap 端口

jmap -heap 11249
结果
Attaching to process ID 11249, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.111-b14

using thread-local object allocation.
Parallel GC with 4 thread(s)

Heap Configuration:
   MinHeapFreeRatio         = 0
   MaxHeapFreeRatio         = 100
   MaxHeapSize              = 1073741824 (1024.0MB)
   NewSize                  = 357564416 (341.0MB)
   MaxNewSize               = 357564416 (341.0MB)
   OldSize                  = 716177408 (683.0MB)
   NewRatio                 = 2
   SurvivorRatio            = 8
   MetaspaceSize            = 21807104 (20.796875MB)
   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   MaxMetaspaceSize         = 17592186044415 MB
   G1HeapRegionSize         = 0 (0.0MB)

Heap Usage:
PS Young Generation
Eden Space:
   capacity = 354418688 (338.0MB)
   used     = 158615424 (151.2674560546875MB)
   free     = 195803264 (186.7325439453125MB)
   44.75368522328033% used
From Space:
   capacity = 1572864 (1.5MB)
   used     = 917600 (0.875091552734375MB)
   free     = 655264 (0.624908447265625MB)
   58.339436848958336% used
To Space:
   capacity = 1572864 (1.5MB)
   used     = 0 (0.0MB)
   free     = 1572864 (1.5MB)
   0.0% used
PS Old Generation
   capacity = 716177408 (683.0MB)
   used     = 341764080 (325.93162536621094MB)
   free     = 374413328 (357.06837463378906MB)
   47.720589365477444% used

27253 interned Strings occupying 3025968 bytes.
分析

由 MaxHeapSize = 1073741824 (1024.0MB)
可知堆最大为1g。那么问题肯定不在堆空间中。
堆空间存放Java运行时创建的对象实例
由MaxMetaspaceSize = 17592186044415 MB,可知元空间最大大小很大,随着程序的运行,元空间大小基本不受限制。
所以问题可能在于元空间占用了大部分内存。
元空间存放的信息:类的class信息,静态变量(基础类型),常量(基础类型),符号引用(方法引用)、字面量(字符串)等。

4、java应用的gc情况
命令

jstat -gc 端口 统计时间 统计次数

jstat -gc 11249 5s 3
结果
 S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT     GCT   
1536.0 1536.0  0.0   896.1  346112.0 36865.6   699392.0   333754.0  9889772.0 9885567.6 667904.0 667103.8  28492  750.752  19      9.213  759.965
1536.0 1536.0  0.0   896.1  346112.0 43401.3   699392.0   333754.0  9889772.0 9885567.6 667904.0 667103.8  28492  750.752  19      9.213  759.965
1536.0 1536.0  0.0   896.1  346112.0 48964.2   699392.0   333754.0  9889772.0 9885567.6 667904.0 667103.8  28492  750.752  19      9.213  759.965

分析

OC = 699392.0 当前老年代已分配内存
OU = 333754.0 当前老年代使用内存
MC =9889772.0 当前元空间已分配内存
MU =9885567.6 当前元空间使用内存
注意单位带小数点肯定不是字节,结合堆栈信息中的Old区,中used可知,当前单位是kb。所以呢元空间占用了近10g左右。

PS Old Generation
   capacity = 716177408 (683.0MB)
   used     = 341764080 (325.93162536621094MB)

结论:元空间占用了大部分内存。
问题?元空间为什么占用了那么多的内存。
需求:知道元空间内部的存储情况。 但是目前没有办法。

由于目前不知道元空间内存情况,但是能够实时监控到元空间的总内存大小。且程序目标范围小,所以可以动态监控,找到出问题的程序。

执行程序动态监控排查问题

找到问题代码部分抽出来,用方法调用循环执行。
监控工具 jvisualvm.exe 。该工具在jdk自带的安装目录下bin文件夹中。
监控结果如下
符合生产情况,符合预期
证明该部分代码存在相关问题。

删除部分代码。继续执行。如果图形还是如上面符合一致性。就证明目标代码还在。如果如下面这种模型就是目标代码在删除的部分。

目标代码在刚删除的部分
重上面办法就可以定位到代码片段。
最终确定代码为
mapper.map(vo, vo.getClass());

        MemberDTO vo = new MemberDTO();
        MapperFactory factory = new DefaultMapperFactory.Builder().build();
        MapperFacade mapper = factory.getMapperFacade();
        mapper.map(vo, vo.getClass()); //本行是有问题的代码,其他行是辅助

动态创建类时候发生的情况
猜测原因,中间创建了一批动态生成的class对象。导致元空间一值增加。
验证,找到类加载器创对象是后的class,看看是否有动态class对象产生。
构造器类对象debug Constructor
类构造器断点调试图
关注类加载器

元空间垃圾中class信息回收条件

Java 堆永久代的回收

回收废弃常量与回收 Java 堆中的对象非常类似。
以常量池中字面量的回收为例,假如一个字符串"abc"已经进入了常量池中,但是当前系统没有任何一个 String 对象是叫做"abc"的,也没有其他地方引用了这个字面量,如果这时发生内存回收,而且必要的话,这个"abc"常量就会被系统清理出常量池。常量池中的其他类(接口)、方法、字段的符号引用也与此类似。
类需要同时满足下面 3 个条件才能算是“无用的类”:
1. 该类所有的实例都已经被回收,也就是 Java 堆中不存在该类的任何实例。
2. 该类对应的 java.lang.Class 对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
3. 加载该类的 ClassLoader 已经被回收。

动态生成的class对象肯定会有一串动态生产的代码。经过几次运行,判断该对象是动态产生的,顾定位到问题。
(MapperFacade )mapper.map(vo, vo.getClass());
每次执行该方法都会动态产生一个class对象导致方法区内存增加。且类加载器classLoader=appClassLoad,应用类加载器。
该类加载器不会被回收,所以class对象也不会被回收。所以元空间的内存会一直增加。类对象站的字节越大,调用次数越多,所占空间越大。

另外附带CGLib动态代理也会出现类似问题。debug如下

在这里插入图片描述在这里插入图片描述
CGLib 元空间内存增长图
CGLib 元空间内存增长图

每代理一次,class名称就就有一个序号增长。

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值