• JVM调优经历


    一、JVM内存溢出问题

    1. 程序报错
      10:06:01 [WARN ] i.n.util.concurrent.DefaultPromise java.lang.OutOfMemoryError: Java heap space

    2. 出现场景
      程序导出数据库中数据,导出5W数据,每次查询1000.
      for循环中创建大量对象,导致内存溢出,怀疑短时间内创建大量对象,内存回收不及时导致堆溢出。

    3. 预测需要内存大小

      System.setProperty("java.vm.name","Java HotSpot(TM) ");
      System.out.println(ObjectSizeCalculator.getObjectSize(dataList));
      
      • 1
      • 2

      可以得出对象占用大小,
      单次循环内 1000条数据占用内存 2,345,568 。 约 2.3M。

      需要导出5w条数据,循环50次。 每次循环结束,创建的对象都会失效回收,最差情况需要最大内存 115M

    4. 查看机器中Java程序堆内存的默认初始大小和最大大小

      -Xmx 最大堆内存的默认值为当前机器最大内存的 1/4
      -Xms 初始堆内存的默认值为当前机器最大内存的 1/64

      在Windows里:
      java -XX:+PrintFlagsFinal -version | findstr /i "HeapSize PermSize ThreadStackSize"
      在Linux里:
      java -XX:+PrintFlagsFinal -version | grep -iE 'HeapSize|PermSize|ThreadStackSize'
      
      • 1
      • 2
      • 3
      • 4

      在这里插入图片描述可以看出最大堆内存为503M,初始堆内存为33M,我们的机器内存为2G,符合上述比例。

    二、JVM调优步骤

    1、增大JVM初始堆内存

    -Xms128 -Xmx512m
    
    • 1

    报错:Caused by: java.lang.OutOfMemoryError: GC overhead limit exceeded

    原因:

    执行垃圾收集的时间比例太大,有效的运算量太小。默认情况下,如果GC花费的时间超过 98%,并且GC 回收的内存少于 2%,JVM 就会抛出这个错误。
    假如不抛出 GC overhead limit 错误会发生什么情况呢? 那就是 GC 清理的这么点内存很快会再次填满,迫使 GC 再次执行。这样就形成恶性循环,CPU 使用率一直是 100%,而 GC 却没有任何成果。系统用户就会看到系统卡死。以前只需要几毫秒的操作,现在需要好几分钟才能完成。

    分析:每次for循环结束,都会GC,但是每次回收的内存很少,频繁GC导致CPU利用率过高报错。

    解决办法:

    • 检查程序是否有循环,生成大量对象,优化代码
    • -XX:-UseGCOverheadLimit 禁用这个检查,只是错误信息延后,替换成 java.lang.OutOfMemoryError: Java heap space
    • 增大堆内存

    2、增大堆内存

    -Xms256m -Xmx2560m
    
    • 1

    测试正常,但服务发生多次 Full GC,严重影响性能。

    初步判断为老年代空间不足引发Full GC。
    由于服务主要是循环创建大量对象导致内存问题,每次循环后创建的对象都将失效,部分对象会进入老年代导致老年代空间不足,触发Full GC

    解决方法:

    减少Full GC,增加Minor GC,调整新生代与老年代比例,增大新生代内存。

    3、调整新生代与老年代比例

    设置 -XX:NewRatio, 默认为2,代表新生代与老年代内存比为1:2,新生代占1/3

    -XX:NewRatio=1
    
    • 1

    再次测试,发现Full GC次数大幅减少

    三、其他JVM调优知识

    • Minor GC :清理新生代,Minor GC是最频繁触发的GC,速度也最快的;
    • Major GC :清理老年代,Major GC比Minor GC慢十倍以上,单独触发Major GC只有CMS和G1有这个能力,因为cms和G1处理老年代不需要触发full gc,其他的老年代回收器回收老年代需要触发full gc。
    • Full GC: 清理整个堆空间,包括新生代、永久代和老年代,它会启动老年代收集器和新生代收集器一起工作,全程Stop The World,尽量避免Full GC

    1、Full GC

    Full GC会回收整个堆的内存,包含老年代,新生代,metaspace等,GC的全过程中所有用户线程都是处于暂停的状态。
    触发条件:

    • 老年代空间不足
    • 永久代/metaspace内存空间不足
    • 调用System.gc()会建议系统调用full gc

    2、新生代对象进入老年代条件

    1. 根据对象年龄,每熬过一次GC,年龄+1,达到阈值后进入老年代。通过 -XX:MaxTenuringThreshold设置阈值
    2. 大对象直接进入老年代,通过 -XX:PretenureSizeThreshold设置
    3. 动态年龄判断
  • 相关阅读:
    unity 3d书籍推荐,想自己学习
    激光雷达工作原理简介
    docker容器安装MySQL,navicat无法连接报错(10060/10061错误)
    15:00面试,15:08就出来了,问的问题有点变态。。。
    python:argparse
    在PostGIS中进行点数据的栅格化
    如何在一个月内写完论文?
    【MAPBOX基础功能】16、mapbox叠加图片图层到地图上
    机器学习算法面试知识点解读
    人工智能技术概述_2.人工智能关键技术
  • 原文地址:https://blog.csdn.net/Zhangxg0206/article/details/126135462