深究JVM垃圾回收机制

简介: JVM垃圾回收机制详解

一、回收堆区

垃圾回收器在堆进行垃圾回收前,首先要判断这些对象那些还存活,那些已经“死去”。判断对象是否已“死”有如下几种算法:

1.引用计数法

给对象增加一个引用计数器,每当有一个地方引用它时,计数器就+1;

当引用失效时,计数器就-1;

任何时刻计数器为0的对象就是不能再被使用的,即对象已“死”。

引用计数法实现简单,判定效率也比较高,在大部分情况下都是一个比较好的算法。

但是,在主流的JVM中没有选用引用计数法来管理内存,最主要的原因是引用计数法无法解决对象的循环引用问题。

2. 可达性分析算法

在上面讲了,Java并不采用引用计数法来判断对象是否已“死”,而采用“可达性分析”来判断对象是否存活。

通过一系列称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索走过的路径称为“引用链”,当一个对象到 GC Roots 没有任何的引用链相连时(从 GC Roots 到这个对象不可达)时,证明此对象不可用。以下图为例:

在Java语言中,可作为GC Roots的对象包含以下几种:

  1. 虚拟机栈(栈帧中的本地变量表)中引用的对象。
  2. 方法区中静态属性引用的对象
  3. 方法区中常量引用的对象
  4. 本地方法栈中(Native方法)引用的对象

引用

JDK1.2以前,Java中引用的定义很传统: 如果引用类型的数据中存储的数值代表的是另一块内存的起始地址,就称这块内存代表着一个引用。

这种定义有些狭隘,一个对象在这种定义下只有被引用或者没有被引用两种状态。

JDK1.2之后,Java对引用的概念做了扩充,将引用分为强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)和虚引用(Phantom Reference)四种,这四种引用的强度依次递减。

1.强引用

类似于"Object obj = new Object()"这类的引用,只要强引用还存在,垃圾回收器永远不会回收掉被引用的对象实例。

当内存空 间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足问题。

2.软引用

如果一个对象只具有软引用,那就类似于可有可无的生活用品。

如果内存空间足够,垃圾回收器就不会回收它,如果内存空间不足了,就会回收这些对象的内存。

软引用可用来实现内存敏感的高速缓存。

举例:查看网页,可能后退,那么刚才的网页要不要一直存储,一直存储就是强引用,不存储就是回收,那么折中一下,于是产生了弱引用,当内存空间足够时,就不回收,不足时,再回收。这个根据内存敏感程度而变化而决定是否缓存,就是内存敏感的高速缓存。

3.弱引用

对象拥有更短暂的生命周期。

在gc线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间是否充足,都会回收它的内存。

由于gc是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。

4.虚引用

就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。

如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。

作用: 虚引用主要用来跟踪对象被垃圾回收的活动

区别: 虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列 (ReferenceQueue)联合使用。

当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。

如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。

3.真正判决

要宣告一个对象的真正死亡,至少要经历两次标记过程

如果对象在进行可达性分析之后发现没有与GC Roots相连接的引用链,那它将会被第一次标记并且进行一次筛选。

筛选的条件是此对象是否有必要执行finalize()方法。

没必要:没有覆盖finalize()方法finalize()方法已经被调用过一次了

审判

如果这个对象被判定为有必要执行finalize()方法,那么这个对象将会被放置在一个叫做F-Queue的队列之中,并在稍后由一个虚拟机自动建立的、低优先级的Finalizer线程去执行它

如果对象在finalize()中成功拯救自己:与引用链上的任何一个对象建立起关联关系

那在第二次标记时它将会被移除出"即将回收"的集合,也就暂时逃脱死亡的命运了。

如果对象这时候还是没有逃脱,那基本上它就是真的被回收了。

二、回收方法区

方法区(永久代)的垃圾回收主要收集两部分内容:废弃常量无用类。

废弃常量:

没有任何一个String对象引用常量池中的"abc"常量,也没有其他地方引用这个字面量,如果此时发生GC并且有必要的话,这个"abc"常量会被系统清理出常量池。

常量池中的其他类(接口)、方法、字段的符号引用也与此类似。

无用类

1.该类的所有实例都已经被回收(即在Java堆中不存在任何该类的实例)

2.加载该类的ClassLoader已被回收

3.该类对应的Class对象没有任何其他地方被引用,无法在任何地方通过反射访问该类的方法

三、垃圾回收算法

1.标记-清除算法

首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象

“标记-清除”算法的不足主要有两个

  1. 效率问题:标记和清除这两个过程的效率都不高
  2. 空间问题:标记清除后会产生大量不连续的内存碎片,空间碎片太多可能会导致以后在程序运行中需要分配较大对象时,无法找到足够连续内存而不得不提前触发另一次垃圾收集。

2.复制算法(新生代回收算法)

它将可用内存按容量划分为大小相等的两块,每次只使用其中一块。

当这块内存需要进行垃圾回收时,会将此区域还存活着的对象复制到另一块上面,然后再把已经使用过的内存区域一次清理掉。

因为复制过去后,另一边的内存肯定是连续的了,此时再把使用过得内存区域清理,从而达到了整理的效果。

也就是伊甸园区移动到survive0和survive1区的算法。

但是,伊甸园区的对象都是朝生夕死的,所以并不需要1:1的空间,所以出现了8:1:1的默认比例

3.标记整理算法(老年代回收算法)

复制收集算法在对象存活率较高时会进行比较多的复制操作,效率会变低。因此在老年代一般不能使用复制算法。

而是采用标记整理算法

标记过程仍与“标记-清除”过程一致,但后续步骤不是直接对可回收对象进行清理,而是让所有存活对象向一端移动,然后直接清理掉除存活对象以外的内存。流程图如下:

4.分代收集算法

就是将堆区分开,不同的位置采用不同的算法

新生代中,每次垃圾回收都有大批对象死去,只有少量存活,因此我们采用复制算法;

老年代中对象存活率高,就必须采用"标记-清理"或者"标记-整理"算法。

四 .Minor GC、Major GC、Full GC的区别?

Minor GC 又称为新生代GC 指的是发生在新生代的垃圾回收操作(包括Eden区和Survivor区)。

当年轻代内存空间被用完时,就会触发垃圾回收。这个垃圾回收叫做Minor GC。

Major GC通常是跟full GC是等价的,收集整个GC堆。

但因为HotSpot VM发展了这么多年,外界对各种名词的解读已经完全混乱了

Full GC定义是相对明确的,就是针对整个新生代、老生代、元空间(metaspace,java8以上版本取代perm gen)的全局范围的GC。

针对HotSpot VM GC来看

它里面的GC其实准确分类只有两大种:

Partial GC:并不收集整个GC堆的模式

  • Young GC:只收集年轻代的GC
  • Old GC:只收集老年代的GC。只有CMS的concurrent collection是这个模式
  • Mixed GC:收集整个年轻代以及老年代的GC。只有G1有这个模式

Full GC:收集整个堆,包括young gen、old gen、perm gen(如果存在的话)等所有部分的模式。

目录
相关文章
|
5月前
|
Arthas 存储 算法
深入理解JVM,包含字节码文件,内存结构,垃圾回收,类的声明周期,类加载器
JVM全称是Java Virtual Machine-Java虚拟机JVM作用:本质上是一个运行在计算机上的程序,职责是运行Java字节码文件,编译为机器码交由计算机运行类的生命周期概述:类的生命周期描述了一个类加载,使用,卸载的整个过类的生命周期阶段:类的声明周期主要分为五个阶段:加载->连接->初始化->使用->卸载,其中连接中分为三个小阶段验证->准备->解析类加载器的定义:JVM提供类加载器给Java程序去获取类和接口字节码数据类加载器的作用:类加载器接受字节码文件。
489 55
|
10月前
|
监控 算法 Java
Java虚拟机(JVM)垃圾回收机制深度剖析与优化策略####
本文作为一篇技术性文章,深入探讨了Java虚拟机(JVM)中垃圾回收的工作原理,详细分析了标记-清除、复制算法、标记-压缩及分代收集等主流垃圾回收算法的特点和适用场景。通过实际案例,展示了不同GC(Garbage Collector)算法在应用中的表现差异,并针对大型应用提出了一系列优化策略,包括选择合适的GC算法、调整堆内存大小、并行与并发GC调优等,旨在帮助开发者更好地理解和优化Java应用的性能。 ####
243 27
|
11月前
|
监控 算法 Java
Java虚拟机(JVM)的垃圾回收机制深度解析####
本文深入探讨了Java虚拟机(JVM)的垃圾回收机制,旨在揭示其背后的工作原理与优化策略。我们将从垃圾回收的基本概念入手,逐步剖析标记-清除、复制算法、标记-整理等主流垃圾回收算法的原理与实现细节。通过对比不同算法的优缺点及适用场景,为开发者提供优化Java应用性能与内存管理的实践指南。 ####
|
5月前
|
缓存 算法 Java
JVM深入原理(八)(一):垃圾回收
弱引用-作用:JVM中使用WeakReference对象来实现软引用,一般在ThreadLocal中,当进行垃圾回收时,被弱引用对象引用的对象就直接被回收.软引用-作用:JVM中使用SoftReference对象来实现软引用,一般在缓存中使用,当程序内存不足时,被引用的对象就会被回收.强引用-作用:可达性算法描述的根对象引用普通对象的引用,指的就是强引用,只要有这层关系存在,被引用的对象就会不被垃圾回收。引用计数法-缺点:如果两个对象循环引用,而又没有其他的对象来引用它们,这样就造成垃圾堆积。
157 0
|
5月前
|
算法 Java 对象存储
JVM深入原理(八)(二):垃圾回收
Java垃圾回收过程会通过单独的GC线程来完成,但是不管使用哪一种GC算法,都会有部分阶段需要停止所有的用户线程。这个过程被称之为StopTheWorld简称STW,如果STW时间过长则会影响用户的使用。一般来说,堆内存越大,最大STW就越长,想减少最大STW,就会减少吞吐量,不同的GC算法适用于不同的场景。分代回收算法将整个堆中的区域划分为新生代和老年代。--超过新生代大小的大对象会直接晋升到老年代。
110 0
|
7月前
|
缓存 监控 算法
JVM简介—2.垃圾回收器和内存分配策略
本文介绍了Java垃圾回收机制的多个方面,包括垃圾回收概述、对象存活判断、引用类型介绍、垃圾收集算法、垃圾收集器设计、具体垃圾回收器详情、Stop The World现象、内存分配与回收策略、新生代配置演示、内存泄漏和溢出问题以及JDK提供的相关工具。
JVM简介—2.垃圾回收器和内存分配策略
|
11月前
|
机器学习/深度学习 监控 算法
Java虚拟机(JVM)的垃圾回收机制深度剖析####
本文深入探讨Java虚拟机(JVM)的垃圾回收机制,揭示其工作原理、常见算法、性能调优策略及未来趋势。通过实例解析,为开发者提供优化Java应用性能的思路与方法。 ####
238 28
|
10月前
|
算法 网络协议 Java
【JVM】——GC垃圾回收机制(图解通俗易懂)
GC垃圾回收,标识出垃圾(计数机制、可达性分析)内存释放机制(标记清除、复制算法、标记整理、分代回收)
|
10月前
|
存储 监控 算法
Java虚拟机(JVM)垃圾回收机制深度解析与优化策略####
本文旨在深入探讨Java虚拟机(JVM)的垃圾回收机制,揭示其工作原理、常见算法及参数调优方法。通过剖析垃圾回收的生命周期、内存区域划分以及GC日志分析,为开发者提供一套实用的JVM垃圾回收优化指南,助力提升Java应用的性能与稳定性。 ####
|
11月前
|
监控 算法 Java
Java虚拟机垃圾回收机制深度剖析与优化策略####
【10月更文挑战第21天】 本文旨在深入探讨Java虚拟机(JVM)中的垃圾回收机制,揭示其工作原理、常见算法及参数调优技巧。通过案例分析,展示如何根据应用特性调整GC策略,以提升Java应用的性能和稳定性,为开发者提供实战中的优化指南。 ####
138 5