JVM(四):GC垃圾回收算法

简介: JVM(四):GC垃圾回收算法

垃圾回收器与内存分配策略

1.1 起源

Lisp是第一门开始使用内存动态分配和垃圾收集技术的语言

作者John McCarthy思考过垃圾收集需要完成的哪三件事?

  • 哪些内存需要回收?
  • 什么时候回收?
  • 如何回收?

程序计数器、虚拟机栈、本地方法区随线程而生,随线程而灭,栈中的栈帧随着方法的进入和退出,有条不紊的执行着出栈和进栈的操作,这几个内存分配具有确定性,不需要考虑内存回收的问题

堆和方法区这两个区域则由很多的不确定性:一个接口的多个实现类内存需要可能不一样…

只有处于运行期间,我们才能知道程序创建哪些对象,创建多少个对象,这部分内存的分配和回收是动态的。垃圾收集器关注的正是这部分内存该如何存储

1.2 对象已死?

在堆中存放Java的所有对象实例,垃圾收集器在对堆进行回收前,第一件事就是要确定哪些对象是死的,哪些对象是存活的。

1.2.1 引用计数算法

判断对象是否存活的条件:在对象中添加一个引用计数器,每当有一个地方引用时,引用计数器+1,当引用失效时,引用计数器-1,任何时刻引用计数为零的对象就是不可能再被使用的

在其他语言中,有的使用了引用计数算法,但在Java中,我们就没有使用该算法,原因是:这个看似简单的算法,必须要配合大量的处理才能合理的运行,比如:对象之间的循环引用,基本无法解决

public class YinYong {
  public Object instance = null;
  public static void main(String[] args) {
    YinYong test1 = new YinYong();
    YinYong test2 = new YinYong();
    test1.instance = test2;
    test2.instance = test1;
    test1 = null;
    test2 = null;
    System.gc();
  }
}

关于上面这个循环引用的算法,《深入理解Java虚拟机》并没有介绍特别清楚,这里详细介绍一下

线程私有区

  • 程序计数器:记录正在执行的虚拟机字节码的地址
  • 虚拟机栈:方法执行的内存区,每个方法执行时都会在虚拟机栈中创建栈帧
  • 本地方法栈:虚拟机的Native方法执行的内存区

线程共享区

  • Java堆:对象分配的区域,这是垃圾回收的主要区域
  • 方法区:存放着类信息、常量、静态变量等数据、常量池

1.2.2 可达性算法

算法思路:通过一系列成为"GC Roots"的根对象作为起始节点集,从这些节点开始,根据引用关系向下搜索,搜索过程中的路径被称为"引用链",如果某个对象到GC Roots之间没有引用链连接的话或者图论也就是不可达,那么这个对象就可以被回收了

GC Root的对象:

  • 虚拟机栈中引用的对象
  • 方法区中类静态属性引用的对象
  • 常量池中引用的对象
  • 本地方法栈JNI,也就是Native引用的对象

1.2.3 四大引用

无论是引用计数法还是可达性分析,都离不开引用这个词,在Java中引用主要有强引用、软引用、弱引用、虚引用。

  • 强引用—不回收、 StrongReference
    对于强引用来说,是我们经常使用的大部分都是强引用,如果一个对象具有强引用,那就类似于必不可少的生活用品,垃圾回收器不会回收它。当我们的空间不足时JVM宁愿抛出OutOfMemoryError也就是内存溢出,让程序终止,也不会靠随意的回收具有强引用的对象来解决内存不足的问题
StringBuffer buffer = new StringBuffer();
StringBuffer buffer1 = buffer;
  • 对于一个普通的对象,如果没有其他引用的关系,只要超过了引用的作用域或者将强引用赋值为null,就是可以当做垃圾被收集了
  • 软引用—有用但非需、内存不足既回收、 SoftReference
  1. 内存足够时,不会回收软引用的可达对象
  2. 当内存不够时,就会进行回收可达对象,如果回收完之后,内存还不够,就会报OOM
user u1 = new user( 1,"songhk");
 softReference<User> userSoftRef = new SoftReference<User>(u1);
 ul = null;
  • 软引用通常实现缓存。比如:图片缓存和网页缓存用到软引用
    如果还有空间,就可以暂时保留缓存,当内存不足时清理掉,这样就保证了使用缓存的同 时,不会耗尽内存
  • 弱引用----发现既回收、WeakReference
    弱引用也是用来描述那些非必需对象,只被弱引用关联的对象只能生存到下一次垃圾收集发生为止。在系统GC时,只要发现弱引用,不管系统堆空间使用是否充足,都会回收掉只被弱引用关联的对象。
    例子:存储可有可无的数据:
    WeakHashMap:内存不足时就会被回收,内部的Entry继承类WeakReference
  • 虚引用——对象回收跟踪、Phantom Reference
    一个对象是否有虚引用的存在,完全不会决定对象的生命周期。如果一个对象仅有虚引用,那么它和没有引用几乎是一样的,随时都可能被垃圾回收器回收
    它不能单独使用,必须和引用队列( ReferenceQueue ),当垃圾回收器准备回收对象时,如果发现他还有虚引用,就会在回收对象的回收之前,把这个虚引用加入到引用队列中,从而可以查看当前JVM垃圾回收的情况

1.2.4 finalize()方法

如果一个对象经过我们可达性算法的计算,判定为不可达的对象,并不是直接将其进行杀死 ,而是进入到一个缓刑的阶段

真正宣告一个对象是否死亡要经历两次标记过程:

  • 如果对象在进行可达性分析后,发现对象不可达,则将会进行第一次标记,随后进行一次筛选,条件为:是否有必要执行finalize方法,如果对象没有重写finalize方法或者finalize已经被调用一次,JVM判定没有必要执行,直接进行回收
  • 如果JVM判定有必要执行finalize的话,会将其放在一个F-Quene的队列中,并执行对象的finalize方法,同样JVM对队列中的对象进行标记。如果这时候对象的finalize方法中,将自己与引用链上的任何一个对象建立了联系,那样,JVM就会在第二次标记的时候将它移出“即将回收”的集合

建议:因为finalize方法的出现具有一定的戏剧色彩,为了使C、C++的程序猿更容易接受而做出的妥协

1.2.5 回收方法区

方法区主要回收两部分内容:废弃的常量和不再使用的类型

而对于一个类型是否被回收就比较困难,需要满足三个条件:

  • 所有的实例都被回收,也就是堆中不存在该类和派生子类
  • 加载该类的加载器也被回收(基本不可能达成)
  • 该类在java.lang.Class对象没有在任何地方引用

1.3 垃圾回收算法

垃圾收集算法可以划分为”引用计数式垃圾收集“和”追踪式垃圾收集“

1.3.1 分代收集理论

两个假说:

  • 弱分代假说:绝大多数的对象都是朝生夕灭的
  • 强分代假说:熬过越多次垃圾收集过程的对象就越难以消亡

设计原则:收集器应该将Java堆划分不同的区域,然后将回收对象依据其年龄分配到不同的区域进行存储

设计者将堆分为新生代和老年代两个区域,本来这种想法挺好的,但是出现了一个问题,也就是对象之间会存在跨代引用

我们要对新生代的对象进行垃圾的收集,但某个对象引用了老年代中的数据,不得不再去遍历老年代中的对象来确保可达性分析结果的准确性

所以,为这个理论增添了第三条经验法则:

  • 跨代引用假说:跨代引用相对于同代引用来说,仅占少数

基于这条假说,我们对于那些隔代引用的对象,不再去单独的扫描他们,而是将他们放在一个新生代记忆集的数据结构中

部分收集—Partial GC

  • 新生代收集:目标是新生代的
  • 老年代收集:目标是老年代的 CMS收集器
  • 混合收集:目标整个新生代和部分老年代 G1收集器

整堆收集----Full GC:收集整个Java堆和方法区的垃圾收集

1.3.2 标记-清除算法

这是最早出现的垃圾清除算法

算法分为两个阶段:先对要回收的对象进行标记,然后再进行回收

缺点:

  • 执行效率不稳定:堆中有大量对象,会进行大量的标记和清除
  • 内部的碎片化问题:标记和清除之后会产生大量的不连续的内存碎片

1.3.3 标记-复制算法

半区复制的垃圾收集算法

将可用内存按容量划分为大小相等的两块,每次使用其中的一块,当这一块的内存用完了,就将还存活着的对象复制到另一块上面,然后再把使用过的内存空间一次性清理掉。

在1989年,提出了一种更加优化的半区复制算法,将新生代分成了一块较大的伊甸园区和两块较小的幸存区

每次分配内存只使用伊甸园和一块幸存区,发生垃圾收集时,将伊甸园和幸存区存活的对象一次性复制到另外一个幸存区,然后直接清理掉伊甸园和已用过的幸存区。

在新生代中的对象存活比较少,所以可以一次性复制到幸存区,但是如果万一超过了内存,怎么办?

需要依赖其他内存区域,也就是老年代进行内存分配担保

优点:不会产生内存碎片

缺点:内存间赋值开销 只有一半空间可用 空间浪费太多

1.3.4 标记-整理算法

标记过程仍然使用标记-清除,后序步骤将所有存活的对象都向内存空间一端移动,然后清理掉边界以外的内存

缺点:在每次移动的过程中,尤其是对老年代这种存活对象多的区域,会导致效率的降低

优点:没有了内存碎片的产生


相关文章
|
4月前
|
Arthas 存储 算法
深入理解JVM,包含字节码文件,内存结构,垃圾回收,类的声明周期,类加载器
JVM全称是Java Virtual Machine-Java虚拟机JVM作用:本质上是一个运行在计算机上的程序,职责是运行Java字节码文件,编译为机器码交由计算机运行类的生命周期概述:类的生命周期描述了一个类加载,使用,卸载的整个过类的生命周期阶段:类的声明周期主要分为五个阶段:加载->连接->初始化->使用->卸载,其中连接中分为三个小阶段验证->准备->解析类加载器的定义:JVM提供类加载器给Java程序去获取类和接口字节码数据类加载器的作用:类加载器接受字节码文件。
400 55
|
9月前
|
监控 算法 Java
Java虚拟机(JVM)垃圾回收机制深度剖析与优化策略####
本文作为一篇技术性文章,深入探讨了Java虚拟机(JVM)中垃圾回收的工作原理,详细分析了标记-清除、复制算法、标记-压缩及分代收集等主流垃圾回收算法的特点和适用场景。通过实际案例,展示了不同GC(Garbage Collector)算法在应用中的表现差异,并针对大型应用提出了一系列优化策略,包括选择合适的GC算法、调整堆内存大小、并行与并发GC调优等,旨在帮助开发者更好地理解和优化Java应用的性能。 ####
221 27
|
6月前
|
存储 算法 Java
G1原理—5.G1垃圾回收过程之Mixed GC
本文介绍了G1的Mixed GC垃圾回收过程,包括并发标记算法详解、三色标记法如何解决错标漏标问题、SATB如何解决错标漏标问题、Mixed GC的过程、选择CollectSet的算法
G1原理—5.G1垃圾回收过程之Mixed GC
|
4月前
|
缓存 算法 Java
JVM深入原理(八)(一):垃圾回收
弱引用-作用:JVM中使用WeakReference对象来实现软引用,一般在ThreadLocal中,当进行垃圾回收时,被弱引用对象引用的对象就直接被回收.软引用-作用:JVM中使用SoftReference对象来实现软引用,一般在缓存中使用,当程序内存不足时,被引用的对象就会被回收.强引用-作用:可达性算法描述的根对象引用普通对象的引用,指的就是强引用,只要有这层关系存在,被引用的对象就会不被垃圾回收。引用计数法-缺点:如果两个对象循环引用,而又没有其他的对象来引用它们,这样就造成垃圾堆积。
123 0
|
4月前
|
算法 Java 对象存储
JVM深入原理(八)(二):垃圾回收
Java垃圾回收过程会通过单独的GC线程来完成,但是不管使用哪一种GC算法,都会有部分阶段需要停止所有的用户线程。这个过程被称之为StopTheWorld简称STW,如果STW时间过长则会影响用户的使用。一般来说,堆内存越大,最大STW就越长,想减少最大STW,就会减少吞吐量,不同的GC算法适用于不同的场景。分代回收算法将整个堆中的区域划分为新生代和老年代。--超过新生代大小的大对象会直接晋升到老年代。
94 0
|
6月前
|
缓存 监控 算法
JVM简介—2.垃圾回收器和内存分配策略
本文介绍了Java垃圾回收机制的多个方面,包括垃圾回收概述、对象存活判断、引用类型介绍、垃圾收集算法、垃圾收集器设计、具体垃圾回收器详情、Stop The World现象、内存分配与回收策略、新生代配置演示、内存泄漏和溢出问题以及JDK提供的相关工具。
JVM简介—2.垃圾回收器和内存分配策略
|
6月前
|
存储 算法 Java
G1原理—6.G1垃圾回收过程之Full GC
本文详细探讨了G1垃圾回收器对Full GC(FGC)的优化处理,涵盖FGC的前置处理、整体流程及并行化改进。重点分析了传统FGC串行化的局限性以及G1通过Region分区和RSet机制实现并行标记的优势,包括任务窃取提升效率、跨分区压缩以生成空闲Region等技术细节。此外,文章还介绍了G1的新特性——字符串去重优化,通过判断char数组一致性减少重复字符串占用内存,从而提升内存使用效率。总结部分全面回顾了G1在FGC中的各项优化措施及其带来的性能改善。
G1原理—6.G1垃圾回收过程之Full GC
|
6月前
|
存储 算法 Java
G1原理—4.G1垃圾回收的过程之Young GC
本文详细解析了G1垃圾回收器中YGC(Young Generation Collection)的完整流程,包括并行与串行处理阶段。内容涵盖YGC相关参数设置、YGC与Mixed GC及FGC的关系、新生代垃圾回收的具体步骤(如标记存活对象、复制到Survivor区、动态调整Region数量等),以及并行阶段的多线程操作和串行阶段的关键任务(如处理软引用、整理卡表、重构RSet)。
G1原理—4.G1垃圾回收的过程之Young GC
|
6月前
|
监控 算法 Java
JVM—垃圾收集算法和HotSpot算法实现细节
JVM的垃圾收集算法和HotSpot的实现细节复杂但至关重要,通过理解和掌握这些算法,可以为Java应用程序选择合适的垃圾收集器,并进行有效的性能调优。选择适当的垃圾收集策略,结合合理的内存配置和日志分析,能够显著提升应用的运行效率和稳定性。
100 15
|
10月前
|
机器学习/深度学习 监控 算法
Java虚拟机(JVM)的垃圾回收机制深度剖析####
本文深入探讨Java虚拟机(JVM)的垃圾回收机制,揭示其工作原理、常见算法、性能调优策略及未来趋势。通过实例解析,为开发者提供优化Java应用性能的思路与方法。 ####
217 28

热门文章

最新文章