JVM垃圾回收器
图
每一个线程会有一个栈,用于放方法、引用等信息,而堆中放了实际的对象数据,栈帧所指向的方法执行完后会自动释放(移动栈帧),但是堆中的对象并不能很好的管理,于是出现了垃圾,浪费了内存空间

垃圾的产生

每一个线程会有一个栈,用于放方法、引用等信息,而堆中放了实际的对象数据,栈帧所指向的方法执行完后会自动释放(移动栈帧),但是堆中的对象并不能很好的管理,于是出现了垃圾,浪费了内存空间

在开发语言中,C/C++的执行效率很高,但是开发效率低,内存难管理,后来出现的开发语言如Java、Python、Go都方便了内存的管理,这里的管理不是指不在出现原有的问题,而是引入了垃圾回收器(GC)

引入GC后,应用线程只管内存的分配,GC线程负责内存的回收

如何判断垃圾

  • 引用计数算法:给对象添加一个计数器,如果被引用,就进行一次计数,当计数为0则进行回收,这个方法无法解决循环依赖问题
  • 根可达算法:当一个对象无法链接到根节点即被认定为垃圾,解决了循环依赖问题

根的定义

  • 栈中的引用
  • 静态变量中的引用
  • 常量池中的引用
  • JNI(Java Native Interface)中的引用

垃圾回收算法

  • 标记清除
    • 分为标记阶段和清除阶段,标记阶段先通过根节点(GC Roots)标记所有从根节点开始的对象,未被标记的对象就是垃圾对象;然后在清除阶段清除未被标记的对象
    • 优点是算法简单,缺点是内存容易产生碎片化,扫描了空间两次
  • 拷贝算法
    • 不管内存多大,一分为二,但一半内存满了后,从根节点开始扫描,标记所有不是垃圾的对象并复制到一块新的内存中,然后将原来的内存块全部清除
    • 优点是算法简单执行效率高,解决了碎片化问题,缺点是浪费内存,有一半没用到
  • 标记压缩
    • 在进行扫描的时候将有用的对象往前挪动,再进行清除
    • 优点是没有碎片化,没有浪费空间,缺点是算法效率低(对象产生了挪动)

三种算法各有优劣势,Java的做法是都使用,结合成了不同的垃圾回收器

堆内存模型

JDK1.8及以前使用的是分代垃圾收集器,之后不再分代

图

针对不同的分代采用不同的垃圾回收算法,新生代采用拷贝算法,老年代采用标记压缩或标记压缩和标记清除的组合

一般来说,新产生的对象一次回收会清除掉90%,每次回收将伊甸园区中存活的对象放入一个幸存者区,回收掉伊甸园区和另一个幸存者区,同理,第二次回收也是将伊甸园区中存活的对象放入空的那个幸存者区,回收掉伊甸园区和原有对象的幸存者区,这样的效率最高,当一个对象多次扫描后依然存活,就会放入老年区,不再进行扫描

GC的演化

图

  • Serial + Serial Old
    • 一开始内存只有几兆到几十兆,使用单线程STW(stop-the-word)即可完成垃圾的回收,回收时停止所有处理
  • Parallel Scavenge + Parallel Old
    • 当内存到几十兆到1G,单线程处理会造成过长的停止时间,于是采用多线程进行垃圾回收,同样会停止所有处理,Java8默认的GC机制
  • Concurrent GC
    • 当内存有几十G时,更多的线程反而效率低下,所以采用同时回收方式,业务线程和垃圾回收线程可以同时工作,不在STW,常见的CMD、ParNew、G1、ZGC、Shenansosh回收器都是该机制
    • CMS从诞生就没有成为默认的回收器,虽然具有并发执行、低延迟等优点,但其内存碎片、停顿时间较长、对CPU资源需求高、无法处理永久代以及无法处理超大对象和数组等缺点

三色标记算法

发生在Concurrent GC并发标记阶段,在三色标记算法中,黑色表示“已处理”,灰色表示“可达但未处理”,白色表示“未访问过”。当垃圾回收器需要回收内存时,它只回收白色对象,因为黑色对象不再需要被回收。而灰色对象则需要继续被遍历,直到它们变为黑色

G1回收器

图

G1在物理上不分区,在逻辑上进行分区。G1将堆内存分为许多个大小相等的区域(Region),每个区域的大小通常为1MB到32MB不等。G1将整个堆空间分成Young区、Survivor区和Old区。其中,Young区和Survivor区用于存放新生成的对象,而Old区则用于存放生命周期较长的对象

G1使用了一种增量收集的方式来进行垃圾回收,这意味着它会根据需要逐步回收内存,而不是一次性地回收整个堆内存。当某个区域中的垃圾占据了该区域总容量的一定比例时,G1就会选择该区域作为下一轮垃圾回收的目标

G1将所有的区域都看做是一个整体进行管理,因此可以避免内存碎片的问题。此外,由于G1只对部分区域进行处理,所以每次垃圾回收所需的时间也大大减少,从而提高了系统的响应速度和吞吐量

PS + PO

Java8默认的垃圾回收器为Parallel Scavenge + Parallel Old

什么是调优

  • 根据需求进行JVM规划和预调整
  • 优化JVM环境运行慢、卡顿问题
  • 解决JVM运行过程中的问题,如:OOM