12 11 2023

垃圾回收算法

引用计数法

如果一个对象没有任何一个与之相关联的引用,则表示可以被回收。
缺点:存在循环引用问题无法回收,导致内存泄漏和溢出。

可达性分析算法

通过GC Root对象,向下寻找,看某个对象是否可达。该算法可解决循环依赖问题。

标记清除算法

分为标记和清除两个部分,会先标记出所有需要回收的对象,然后统一进行回收。
缺点:

  • 标记和清除比较耗时,效率不高,很少被使用
  • 会产生大量不连续的内存碎片

标记复制算法

将内存划分两块相等的区域,每次只使用其中一块。当这块内存不足时候,将还存活的对象复制到另一块上面,然后把已使用过的内存块一次清理掉。
缺点:不会产生内存碎片这种问题,但可用内存缩小一半,空间利用率比较低,所以只在新生代采用这个算法,避免浪费。

标记整理算法

标记整理算法,它可以解决内存碎片的问题但效率会更慢。在标记结束后还需要整理所有存活对象的内存地址因此耗时较长。
标记复制算法和标记整理算法它们分别解决了内存效率和空间碎片的问题但后者效率更慢。

分代收集算法

Young区:标记复制算法(对象在被分配之后,可能生命周期比较短,Young区复制效率比较高)
Old区:标记清除或者标记整理(Old区对象存活时间比较长,复制来复制去没必要,不如做个标记再清理)。

垃圾收集器分类

串行收集器
包括:serial 和 serial old。
说明:只能有一个垃圾回收线程执行,用户线程暂停。适用于内存较少的嵌入式设备。
并行收集器(吞吐量优先)
包括:Parallel Scanvenge、Parallel Old
说明:多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态。适用于科学计算、后台交互场景。
并发收集器(停顿时间优先)
包括:CMS、G1
说明:用户线程和垃圾收集线程同时执行(但并不一定是并行的,可能是交替执行的),垃圾收集线程在执行的时候不会停顿用户线程的运行。
适用于相对时间有要求的场景,比如Web。
G1从整体来看是基于 标记-整理 算法实现的回收器,但从局部(两个Region之间)上看又是基于 标记-复制 算法实现的。

CMS收集器
说明:CMS采用标记清除算法实现,被G1替代已不推荐使用。
优点:并发收集、低延迟 。
缺点:标记清除算法,会产生内存碎片;对CPU资源非常敏感,消耗高;无法处理浮动垃圾。

G1收集器
说明:是一个并行回收器,它把堆内存分割为很多不相关的区域(Region),在后台维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的Region。
优点:与其他GC收集器相比,G1使用了全新的分区算法。并行与并发,不会造成应用程序阻塞;分代收集;空间整合(若干次GC后进行一次碎片整理);可预测的停顿时间模型;

如何选择合适的垃圾收集器?

  • 优先调整堆的大小让服务器自己来选择
  • 如果内存小于100M,使用串行收集器
  • 如果是单核,并且没有停顿时间要求,使用串行或JVM自己选
  • 如果允许停顿时间超过1秒,选择并行或JVM自己选
  • 如果响应时间最重要,并且不能超过1秒,使用并发收集器

如何判断是否使用G1收集器?

  1. 超过 50%的Java 堆被实时数据占用。
  2. 对象分配率或提升率差异很大。
  3. 垃圾回收时间比较长。

JVM常用命令?

  • jps : 查看Java进程
  • jinfo :查看进程对于name属性值。eg. jinfo -flag [name] PID
  • jstat :查看内加载信息,gc信息等。
  • jmap :查看堆内存打印快照。
  • jstack :查看死锁情况分析。eg. jstack PID

JVM调优策略?

(1)不要手动设置新生代和老年代的大小,只要设置整个堆的大小。
(2)不断调优暂停时间目标。
(3)使用 -XX:ConcGCThreads=n来增加标记线程的数量。
(4)不正常的FullG : 有时候会发现系统刚刚启动的时候,就会发生一次Fu11GC,但是老年代空间比较充足,一般是由Metaspace区域引起的。可以通过MetaspaceSize适当增加其大家,比如256M。

延伸阅读