JVM 调优实战--常见的垃圾回收算法及垃圾收集器组合

什么是垃圾

C语言申请内存:malloc free

C++: new delete

c/C++ 手动回收内存

Java: new ?

自动内存回收,编程上简单,系统不容易出错,手动释放内存,容易出两种类型的问题:

  1. 忘记回收

  2. 多次回收

没有任何引用指向的一个对象或者多个对象(循环引用)

如何定位垃圾

①引用计数法

②跟可达算法

常见的垃圾回收算法

  1. 标记清除 - 位置不连续 产生碎片 效率偏低(两遍扫描)

  2. 拷贝算法 - 没有碎片,浪费空间

  3. 标记压缩 - 没有碎片,效率偏低(两遍扫描,指针需要调整)

JVM内存分代模型

jdk1.7永久代存放的是各种Class文件,如使用Spring等框架会产生大量代理对象,它们的Class对象就存放在永久代中。

字符串常量:1.7是存放在永久代方法区MethodArea中,1.8存放在堆中。

MethodArea方法区:

①1.7叫做Perm Generation,1.8叫做Metaspace元数据区。

②里面存放的是class的元信息,代码的编译信息,各种层次信息,JIT编译信息(JNI)等凡是跟JVM自身的class无关的其他信息都存放在方法区这个逻辑分区里面。

③在1.7版本,必须指定固定大小,而且很容易溢出。1.8之后内存的大小设置受限于物理内存,既可以设置也可以不设置,不设置理论上在物理内存范围内无上限。

堆内存:包括年轻代和老年代

堆外内存:包括永久代和元数据区(曾用名:方法区)

  1. 部分垃圾回收器使用的模型

    除Epsilon ZGC Shenandoah之外的GC都是使用逻辑分代模型

    G1是逻辑分代,物理不分代

    除此之外不仅逻辑分代,而且物理分代

  2. 新生代 + 老年代 + 永久代(1.7)/ 元数据区(1.8) Metaspace

    1. 永久代 元数据 - Class

    2. 永久代必须指定大小限制 ,元数据可以设置,也可以不设置,无上限(受限于物理内存)

    3. 字符串常量 1.7 - 永久代,1.8 - 堆

    4. MethodArea逻辑概念 - 永久代、元数据

  3. 新生代 = Eden + 2个suvivor区

    1. YGC回收之后,大多数的对象会被回收,活着的进入s0

    2. 再次YGC,活着的对象eden + s0 -> s1

    3. 再次YGC,eden + s1 -> s0

    4. 年龄足够 -> 老年代 (15 CMS 6)

    5. s区装不下 -> 老年代

  4. 老年代

    1. 顽固分子

    2. 老年代满了FGC Full GC

  5. GC Tuning (Generation)

    1. 尽量减少FGC

    2. MinorGC = YGC

    3. MajorGC = FGC

  6. 对象分配过程图

7.动态年龄:(不重要) https://www.jianshu.com/p/989d3b06a49d

8.分配担保:(不重要) YGC期间 survivor区空间不够了 空间担保直接进入老年代

对象的分配过程:

先在栈上跑,也就是栈上分配,看大不大,如果比较大的话就进入Old区,如果不是很大,就先进行线程本地分配, 都进入Eden区,如果能清除就清除,不能清除就进入S1,S1再来一遍,看年龄满了没有,年龄够了就进Old区,如果不够就进入S2,就这样循环往复。

常见的垃圾收集器

常见的使用组合是:

①Serial+Serial Old

②ParNew+CMS

③Parallel Scavenge+Parallel Old (PS+PO jdk1.8默认的)

④G1

这些使用组合没有孰优孰劣,得分不同的场景来选择。比方说,老的机器上可能使用单线程的Serial更合适。

ParNew+CMS是高响应,低停顿,但吞吐量降低。

  1. JDK诞生 Serial追随 提高效率,诞生了PS,为了配合CMS,诞生了PN,CMS是1.4版本后期引入,CMS是里程碑式的GC,它开启了并发回收的过程,但是CMS毛病较多,因此目前任何一个JDK版本默认是CMS 并发垃圾回收是因为无法忍受STW

  2. Serial 年轻代 串行回收

  3. PS 年轻代 并行回收

  4. ParNew 年轻代 配合CMS的并行回收

  5. SerialOld

  6. ParallelOld

  7. ConcurrentMarkSweep 老年代 并发的, 垃圾回收和应用程序同时运行,降低STW的时间(200ms) CMS问题比较多,所以现在没有一个版本默认是CMS,只能手工指定 CMS既然是MarkSweep,就一定会有碎片化的问题,碎片到达一定程度,CMS的老年代分配对象分配不下的时候,使用SerialOld 进行老年代回收 想象一下: PS + PO -> 加内存 换垃圾回收器 -> PN + CMS + SerialOld(几个小时 - 几天的STW) 几十个G的内存,单线程回收 -> G1 + FGC 几十个G -> 上T内存的服务器 ZGC 算法:三色标记 + Incremental Update

  8. G1(10ms) 算法:三色标记 + SATB

  9. ZGC (1ms) PK C++ 算法:ColoredPointers + 写屏障?

  10. Shenandoah 算法:ColoredPointers + 读屏障?

  11. Eplison

  12. PS 和 PN区别的延伸阅读: ▪https://docs.oracle.com/en/java/javase/13/gctuning/ergonomics.html#GUID-3D0BB91E-9BFF-4EBB-B523-14493A860E73

  13. 垃圾收集器跟内存大小的关系

    1. Serial 几十兆

    2. PS 上百兆 - 几个G

    3. CMS - 20G

    4. G1 - 上百G

    5. ZGC - 4T

1.8默认的垃圾回收:PS + ParallelOld

常见垃圾回收器组合参数设定:(1.8)

  • -XX:+UseSerialGC = Serial New (DefNew) + Serial Old

    • 小型程序。默认情况下不会是这种选项,HotSpot会根据计算及配置和JDK版本自动选择收集器

  • -XX:+UseParNewGC = ParNew + SerialOld

  • -XX:+UseConc(urrent)MarkSweepGC = ParNew + CMS + Serial Old(jdk1.8的命令是UseConcMarkSweepGC,有的jdk版本是要加上红色字体部分的。Serial Old是替补)

  • -XX:+UseParallelGC = Parallel Scavenge + Parallel Old (1.8默认) 【PS + SerialOld】

  • -XX:+UseParallelOldGC = Parallel Scavenge + Parallel Old

  • -XX:+UseG1GC = G1

  • Linux中没找到默认GC的查看方法,而windows中会打印UseParallelGC

    • java +XX:+PrintCommandLineFlags -version

    • 通过GC的日志来分辨

  • Linux下1.8版本默认的垃圾回收器到底是什么?

    • 1.8.0_181 默认(看不出来)Copy MarkCompact

    • 1.8.0_222 默认 PS + PO

 

怎么调优

所谓调优,首先要确定追求的是啥?是吞吐量优先还是响应时间优先?或者是在满足一定的响应时间的前提下,要求达到一定的吞吐量?有的放矢才能做好调优这活!

如果是选择吞吐量优先,一般选择的是PS+PO这个组合

如果是响应时间优先,比如网站,GUI,API服务等,1.8版本选择G1垃圾收集器。

jconsole远程连接

  1. 程序启动加入参数:

    java -Djava.rmi.server.hostname=192.168.17.11 -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=11111 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false XXX
    
  2. 如果遭遇 Local host name unknown:XXX的错误,修改/etc/hosts文件,把XXX加入进去

    192.168.17.11 basic localhost localhost.localdomain localhost4 localhost4.localdomain4
    ::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
  3. 关闭linux防火墙(实战中应该打开对应端口)

    service iptables stop
    chkconfig iptables off #永久关闭
  4. windows上打开 jconsole远程连接 192.168.17.11:11111

 

jprofiler (收费)

arthas在线排查工具

为什么需要在线排查? 在生产上我们经常会碰到一些不好排查的问题,例如线程安全问题,用最简单的threaddump或者heapdump不好查到问题原因。为了排查这些问题,有时我们会临时加一些日志,比如在一些关键的函数里打印出入参,然后重新打包发布,如果打了日志还是没找到问题,继续加日志,重新打包发布。对于上线流程复杂而且审核比较严的公司,从改代码到上线需要层层的流转,会大大影响问题排查的进度。

CMS

CMS的问题

  1. Memory Fragmentation

    -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction 默认为0 指的是经过多少次FGC才进行压缩

  2. Floating Garbage

    Concurrent Mode Failure 产生:if the concurrent collector is unable to finish reclaiming the unreachable objects before the tenured generation fills up, or if an allocation cannot be satisfiedwith the available free space blocks in the tenured generation, then theapplication is paused and the collection is completed with all the applicationthreads stopped

    解决方案:降低触发CMS的阈值

    PromotionFailed

    解决方案类似,保持老年代有足够的空间

    –XX:CMSInitiatingOccupancyFraction 92% 可以降低这个值,让CMS保持老年代足够的空间

发布了824 篇原创文章 · 获赞 369 · 访问量 79万+

猜你喜欢

转载自blog.csdn.net/a772304419/article/details/104019079