JVM之垃圾回收算法详解

简介: JVM之垃圾回收算法详解

JVM之垃圾回收算法详解

现有的垃圾回收算法

分类

根据如何判定对象是垃圾,垃圾回收算法分为两类:

1、引用计数式垃圾收集(判定垃圾是通过引用计数器)别名:直接垃圾收集

2、追踪式垃圾收集(判定垃圾是通过GC Roots)别名:间接垃圾收集

主流虚拟机采用的是第二种追踪式垃圾收集,所以本文讲解第二种垃圾收集的算法

垃圾收集器的设计原则

根据两个分代假说:

1.绝大部分对象是熬不过第一次垃圾回收的

2.熬过多次垃圾回收的对象是难以被标记为垃圾的。

垃圾收集器将堆中的内存划分为了不同的区域,根据对象分代年龄(熬过多少次垃圾回收)来分配到不同的区域中:

比如对象分代年龄小的,第一种对象就应该标记存活对象即可,而不需要标记那些垃圾对象,因为这部分对象大部分都是很快用完就不用的垃圾对象。

而第二种对象分代年龄大的,则应该标记的是垃圾对象,因为根据第二个假说这部分对象中垃圾对象的占比很少,所以垃圾回收的频率也可以降低。

将堆划分为不同的区域后,垃圾回收器可以只回收其中一部分区域,针对每一部分区域也可以采用不同的算法来进行回收垃圾。

一般来说堆中至少会被划分为“新生代”和“老年代”两个区域。新生代存储第一种假说类型的对象,老年代存放第二种假说类型的对象。

注意:这种设计看起来是完美的,但是如果老年代中的对象引用了新生代中的对象这个时候年轻代发生垃圾回收时,除了需要遍历GC Roots外,还需要遍历整个老年代才会确保年轻代中的对象真正没有对象引用。显然这种遍历整个老年代效率肯定会很低,所以采用了一种解决方案:读者有兴趣可以看看: 在这篇博客的末尾

标记-清除算法

最早出现的垃圾回收算法,之后出现的算法都是根据其缺点来进行演进的。

两个阶段:

1.标记

2.清除

标记需要回收的对象完成后进行统一回收所有被标记的对象,
也可以标记存活的对象统一回收没有被标记的对象。

一,标记

如何判定对象是否是垃圾的过程在上一篇 博客 中已经讲解过,接着标记这些垃圾对象。

二,清除

进行统一回收掉标记的对象。

缺点

1.当堆中的对象大部分是垃圾时,标记和清除的效率会变低,而且会随着内存中垃圾对象的增长,导致效率越来越低。

2.内存碎片化:因为内存分配不是连续的,所以当清除后,内存中会存在大量内存碎片。当遇到大对象分配内存找不到足够的连续的内存来存放时会提前触发GC。

标记-复制算法

采用的是“半区复制”的算法来实现的,即每次只使用其中的一部分内存,当这部分内存用完后将存活着的对象复制到另外一块内存上,接着清空刚才使用的那部分内存,当另一部分内存满了的时候再用上一次清空后的那块内存往复。

解决了标记-清除的内存碎片化问题,因为当发生GC时会进行全部清空,只将存活对象复制到另外一块内存中。

“Apple回收策略”

Andrew Appel针对刚刚分代假说中的第一条,提出了“Appel式回收策略”。

一般情况下百分之九十八的对象在经历第一次gc时就会被清除。因此做出优化将年轻代分为了一块eden空间和两块Survival空间。enen和Survival内存占比为8:1,即每次使用百分之九十的内存,只有百分之十的内存会被浪费,因为对象大部分都会死去所以没有必要分配一半的空间来存放存活对象。

但是如果使用百分之十的内存来存放存活对象,当存活对象在Survival空间存放不下时,这个时候就需要用老年代担保,因此当存不下时会存放到老年代中。

缺点

1.当内存中的对象存活率较高时,复制大量存活对象会使得效率变低。

2.如果不想造成严重的内存浪费,就需要有额外的空间进行分配。

标记-整理算法

上面所说的标记-复制算法针对与新生代中进行的回收算法,并不适用与老年代,原因:老年代中存活率较高,存放不下时需要额外的内存空间担保。

因此出现了标记-整理算法,

和标记-复制算法相同的是:标记的都是存活对象

和标记-复制算法不同的是:复制算法将存活对象复制到另外一块内存上然后清除之前使用的内存,而整理算法是移动存活的对象到一端,然后清除边界以外的内存

缺点

移动存活对象

对于老年代中GC之后大部分都为存活对象,将这些对象都进行移动并且更新引用这些对象的地方是一个比较耗时的操作。而且更新引用需要暂停用户线程来保证用户线程访问对象不会出错,简称STW,“Stop the Word”。

其实不止整理算法里面移动对象更新引用需要STW,清除算法和复制算法中的标记清除都需要STW,只不过时间短

总结

采用标记-整理算法意味着GC的时候要移动对象更新对象的引用,也就是说内存回收的时候会更复杂

采用标记-清除算法意味着内存碎片化

采用标记-复制算法意味着内存可用度不高

“吞吐量”:赋值器(使用垃圾回收器的线程也就是用户线程)与垃圾回收器的效率总和。

因为内存分配和访问(内存碎片化导致过多的分配和访问)比垃圾回收器回收的频率(回收时需要STW,而且比较耗时)要高,因此整理这种方式其实吞吐量要比清除要好。

对于关注吞吐量的收集器Parallel Scabenge基于标记-整理算法,

对于关注STW的收集器CMS来说采用的是标记-清除算法。

其实CMS是一种将两种算法混合起来的收集器,大部分时间采用清除算法,只有当分配内存不足(碎片化特别严重)时用整理算法进行一次收集。


目录
相关文章
|
3月前
|
存储 算法 Oracle
极致八股文之JVM垃圾回收器G1&ZGC详解
本文作者分享了一些垃圾回收器的执行过程,希望给大家参考。
|
1月前
|
缓存 算法 Java
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
这篇文章详细介绍了Java虚拟机(JVM)中的垃圾回收机制,包括垃圾的定义、垃圾回收算法、堆内存的逻辑分区、对象的内存分配和回收过程,以及不同垃圾回收器的工作原理和参数设置。
60 4
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
|
1月前
|
存储 监控 算法
美团面试:说说 G1垃圾回收 底层原理?说说你 JVM 调优的过程 ?
尼恩提示: G1垃圾回收 原理非常重要, 是面试的重点, 大家一定要好好掌握
美团面试:说说 G1垃圾回收 底层原理?说说你 JVM 调优的过程  ?
|
1月前
|
算法 Java
JVM进阶调优系列(4)年轻代和老年代采用什么GC算法回收?
本文详细介绍了JVM中的GC算法,包括年轻代的复制算法和老年代的标记-整理算法。复制算法适用于年轻代,因其高效且能避免内存碎片;标记-整理算法则用于老年代,虽然效率较低,但能有效解决内存碎片问题。文章还解释了这两种算法的具体过程及其优缺点,并简要提及了其他GC算法。
 JVM进阶调优系列(4)年轻代和老年代采用什么GC算法回收?
|
1月前
|
算法 Java
谈谈HotSpot JVM 中的不同垃圾回收器
【10月更文挑战第5天】理解 HotSpot JVM 中的不同垃圾回收器(如 CMS、G1 和 ZGC)的区别,需要深入了解它们的设计原理、工作方式和应用场景。以下是对这三个垃圾回收器的简要概述以及一个示例 Java 程序,虽然示例程序本身不能直接展示垃圾回收器的内部机制,但可以帮助观察不同垃圾回收器的行为。
25 1
|
1月前
|
存储 算法 Java
【JVM】垃圾释放方式:标记-清除、复制算法、标记-整理、分代回收
【JVM】垃圾释放方式:标记-清除、复制算法、标记-整理、分代回收
49 2
|
29天前
|
算法 JavaScript 前端开发
垃圾回收算法的原理
【10月更文挑战第13天】垃圾回收算法的原理
22 0
|
2月前
|
存储 算法 Java
深入解析 Java 虚拟机:内存区域、类加载与垃圾回收机制
本文介绍了 JVM 的内存区域划分、类加载过程及垃圾回收机制。内存区域包括程序计数器、堆、栈和元数据区,每个区域存储不同类型的数据。类加载过程涉及加载、验证、准备、解析和初始化五个步骤。垃圾回收机制主要在堆内存进行,通过可达性分析识别垃圾对象,并采用标记-清除、复制和标记-整理等算法进行回收。此外,还介绍了 CMS 和 G1 等垃圾回收器的特点。
112 0
深入解析 Java 虚拟机:内存区域、类加载与垃圾回收机制
|
1月前
|
存储 Java PHP
【JVM】垃圾回收机制(GC)之引用计数和可达性分析
【JVM】垃圾回收机制(GC)之引用计数和可达性分析
58 0
|
3月前
|
存储 算法 Java
JVM自动内存管理之垃圾收集算法
文章概述了JVM内存管理和垃圾收集的基本概念,提供一个关于JVM内存管理和垃圾收集的基础理解框架。
JVM自动内存管理之垃圾收集算法