java内存占用过大问题排查及解决

java内存占用过大问题排查及解决

问题描述:

1.consumer实例堆内存分配4g,xmx设置为4g,在生产环境运行一段时间后,实际占用内存4.8g,业务运行正常,未出现OOM。
2、生产环境项目,均出现运行一段时间后,内存被占满但未OOM的情况。部分实例因内存占用过高导致被系统kill,一般需要通过增加机器、实例进行解决(资源浪费)。
造成的影响
3、服务器物理内存8g,部署了两个服务。consumer如过实际占用内存都超过4.8g,导致服务器物理内存不够用,出现告警而将占用内存最大进程kill掉,影响生产服务的可用性,后果十分严重。
4、如服务申请的内存超出了JVM能提供的内存大小(内存泄漏),将会导致java堆内存溢出,从而发生full gc,导致服务响应大幅度变慢,卡机等状态。

top 命令查看内存占用情况

jmap -heap 30788
jstat -gc 30788 查看各区域占用内存情况
jmap -dump:format=b,file=mall.dump 30788 生成dump文件
查看dump文件,看了一下文件占用,没有太大的文件占用
在这里插入图片描述
执行jstat -gc前先执行jinfo -flags 线程id查询jvm参数是否配置成功。

一、经调研,逐渐被淘汰的垃圾回收器比如ParallelOldGC和CMS,只要JVM申请过的内存,即使发生了GC回收了很多内存空间,JVM也不会把这些内存归还给操作系统。这就会导致top命令中看到的RSS(进程RAM中实际保存的总内存)只会越来越高,而且一般都会超过Xmx的值。JDK1.9以后。默认的垃圾回收器已经选择了G1。
G1相比CMS有更清晰的优势:
1)CMS采用"标记-清理"算法,所以它不能压缩,最终导致内存碎片化问题。而G1采用了复制算法,它通过把对象从若干个Region(独立区域)拷贝到新的Region(独立区域)过程中,执行了压缩处理,垃圾回收后会整合空间,无内存碎片。
2)在G1中,堆是由Region(独立区域)组成的,因此碎片化问题比CMS肯定要少的多。而且,当碎片化出现的时候,它只影响特定的Region(独立区域),而不是影响整个堆中的老年代。
3)而且CMS必须扫描整个堆来确认存活对象,所以,长时间停顿是非常常见的,无法预测停顿时间。而G1的停顿时间取决于收集的Region(独立区域)集合数量,在指定时间内只回收部分价值最大的空间,而不是整个堆的大小,所以相比起CMS,长时间停顿要少很多,可控很多。
4)G1选回收阶段不会产生“浮动垃圾”,由于只回收部分Region(独立区域),所以STW(stop-The-World机制简称STW,是在执行垃圾收集算法时,Java应用程序的其他所有线程都被挂起)时间我们可控,所以不需要与用户线程并发争抢CPU资源。而CMS并发清理需要占据一部分的CPU,会降低吞吐量。G1由于STW,所以不会产生"浮动垃圾",CMS在并发清理阶段会产生的无法回收的垃圾。
因此在以下场景下G1更适合:
1)服务端多核CPU、JVM内存占用较大的应用。
2)应用在运行过程中会产生大量内存碎片、需要经常压缩空间。
3)想要更可控、可预期的GC停顿周期;防止高并发下应用雪崩现象。
新参数(使用G1做为垃圾回收器)

-Xms4g -Xmx4g -Xss256k -XX:NewSize=512m -XX:MaxNewSize=512m -XX:+UseG1GC -XX:InitiatingHeapOccupancyPercent=40 -XX:G1HeapRegionSize=8m -XX:+ExplicitGCInvokesConcurrent -XX:ParallelGCThreads=4 -Dsun.rmi.dgc.server.gcInterval=36000000 -Dsun.rmi.dgc.client.gcInterval=36000000 -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=256m -XX:+UseCodeCacheFlushing -XX:ReservedCodeCacheSize=256m -XX:MaxDirectMemorySize=512m -XX:GCTimeRatio=19 -XX:MinHeapFreeRatio=20 -XX:MaxHeapFreeRatio=30 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/usr/local/src/gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps

现用:

nohup java -Xmx8g -Xms8g -Xss256k  -jar buyer-api-$version.jar> logs/buyer.out  &

更改后:

nohup java -Xms8g -Xmx8g -Xss256k -XX:NewSize=512m -XX:MaxNewSize=512m -XX:+UseG1GC -XX:InitiatingHeapOccupancyPercent=40 -XX:G1HeapRegionSize=8m -XX:+ExplicitGCInvokesConcurrent -XX:ParallelGCThreads=4 -Dsun.rmi.dgc.server.gcInterval=36000000 -Dsun.rmi.dgc.client.gcInterval=36000000 -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=256m -XX:+UseCodeCacheFlushing -XX:ReservedCodeCacheSize=256m -XX:MaxDirectMemorySize=1g -XX:GCTimeRatio=19 -XX:MinHeapFreeRatio=20 -XX:MaxHeapFreeRatio=30 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/usr/local/src/gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps  -jar buyer-api-$version.jar> logs/buyer.out 2>&1 &

在这里插入图片描述

在使用 G1 垃圾回收器进行内存回收时,不同于传统的垃圾回收器,它使用了一种全局内存回收策略和区域化的垃圾回收方式。G1 将整个 Java 堆分成了不同的区域,每个区域的大小为 1MB 到 32 MB 不等。这使得 G1 比 CMS 垃圾回收器更为高效,可以更好地控制堆内存中的碎片化。
在 G1 中,如果某个区域中没有足够的垃圾需要回收,G1 可以将该区域标记为可用,并回收其中的内存。因此,与 CMS 垃圾回收器等传统的垃圾回收器不同的是,在 G1 中,即使没有足够的垃圾需要回收,JVM 也可能将未使用的内存(例如空闲的区域)返回给操作系统。这将导致在使用 G1 时,进程占用的内存可能要比最大堆大小(通过 -Xmx 参数设置)小很多。
因此,在使用 G1 垃圾回收器时,Java 进程实际占据的内存可能远小于最大堆大小(即 -Xmx 设置的值),即使看起来 Java 进程正在使用的内存比较少,实际它可能已经使用了大部分预分配的内存。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值