常用垃圾收集算法——《深入理解Java虚拟机》笔记

概述

垃圾收集器(Garbage Collection, GC)的历史要比Java久远,且并非Java独有,GC主要完成以下三件事情:

  1. 哪些内存需要回收
  2. 什么时候回收
  3. 如何回收

对于Java内存运行时区域的各个部分,程序计数器、虚拟机栈、本地方法栈3个线程私有区域是随线程而生,又随线程而灭,因此这几个区域的内存分配和回收都具备确定性,不需要考虑垃圾回收的问题。而Java堆和方法区这两个线程共享区的内存是动态分配的,因此垃圾收集器主要关注的是这部分的内存,这里也是垃圾回收的主战场。

如何判断对象死亡

引用计数算法

原理:给对象添加一个引用计数器,当有一个地方引用它时,计数器加1,当引用结束时,计数器减1,任何时候当引用计数为0时,则认为该对象没有被使用,可以被回收。

引用计数算法实现简单,效率也很高,也有很多技术中都使用该算法来管理内存。但Java虚拟机却没有使用该算法,最主要的原因它很难解决对象之间互相循环引用的问题。例如下面示例代码:

public class ReferenceCountingGC {
    public Object instance = null;

    public static void testGC() {
        ReferenceCountingGC objA = new ReferenceCountingGC();
        ReferenceCountingGC objB = new ReferenceCountingGC();
        objA.instance = objB;
        objB.instance = objA;

        objA = null;
        objB = null;

        System.gc();
    }
}

上述代码中objA与objB互相引用,如果Java虚拟机采用引用计数算法,发生GC时,objA、objB将无法被回收。测试可知,上述Java代码在GC时是可以回收内存的,因此可从侧面证明Java虚拟机并非用的引用计数算法。

可达性分析算法

其实在Java、C#等主流语言中,使用的都是可达性分析(Reachability)算法来判定对象是否存活的。该算法基本思路是通过一系列称为“GC Roots”的对象作为起始点,从这些节点向下搜索,搜索所走过的路径叫做引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连时,用图论原理来说,即GC Roots到这个对象是不可达的,则以此判断该对象是不可用的。

可达性分析原理示意图(来自Google I/O)

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

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

常用的垃圾收集算法

标记-清除算法

标记-清除(Mark-Sweep)算法是最基础的垃圾收集算法,其分为两个阶段:首先标记出所需要回收的对象,然后在标记完成后统一回收所有被标记的对象。

标记-清除算法的不足:

  1. 效率问题,标记和清除两个过程的效率都不高
  2. 空间问题,标记清除后会产生大量不连续的内存碎片,空间碎片太多可能导致以后要为大对象分配内存时,如果找不到足够的连续内存而不得不触发另一次垃圾收集动作。

标记-清除算法原理示意图(来自《深入理解Java虚拟机》)

复制算法

复制(Copying)算法为了解决上述算法的效率问题,它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活的对象复制到另一块上面,然后再把已使用过的内存空间一次清空。这样使得每次都是对整个半区进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况,只要移动堆顶指针,按顺序分配内存即可,实现简单,运行高效。

复制算法原理示意图(来自《深入理解Java虚拟机》)

现代商业虚拟机都采用这种收集算法来回收新生代,但按上述原理,将内存缩小为原来的一半使用,这种代价有点太大了。IBM研究证明,98%的对象是“朝生夕死”,所以不必按1:1来划分内存空间,而是将内存分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden空间和其中一块Survivor。当回收内存时,将Eden和第一块Survivor中还存活的对象一次性地复制到第二块Survivor中,然后清理掉Eden和第一块Survivor空间。HotSpot虚拟机默认Eden与Survivor空间的大小比例为8:1,一旦第二块Survivor不足以容纳Eden与第一块Survivor复制过来的存活对象时,这些对象将通过分配担保机制进入老年代。

标记-整理算法

复制收集算法在对象存活率较高时就要频繁进行复制操作,导致效率变低,因此在老年代一般不选用这种算法。根据老年代的特点,有人提出了标记-整理(Mark-Compact)算法,标记过程跟标记-清除算法的一样,但后续步骤不是直接将可回收对象进行清理,而是让所有存活对象都向一端移动,然后直接清理掉端边界以外的内存。

标记-整理算法原理示意图(来自《深入理解Java虚拟机》)

分代收集算法

当前虚拟机的垃圾收集都采用“分代收集”(Generational Collection)算法。算法思想是跟根据对象存活周期的不同将内存划分为几块,一般将Java堆划分为新生代和老年代,这样可以根据各个年代的特点采用最合适的垃圾收集算法。

在新生代中,每次垃圾收集时都有大量对象死亡,只有少量存活,因此优选复制算法,这样只需要付出少量对象的复制成本就可以完成收集;而对于老年代,因为对象存活率高,且没有额外的空间对它进行分配担保,就更适合使用标记-清理或标记-整理算法来进行回收。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
为什么要学JVM1、一切JAVA代码都运行在JVM之上,只有深入理解虚拟机才能写出更强大的代码,解决更深层次的问题。2、JVM是迈向高级工程师、架构师的必备技能,也是高薪、高职位的不二选择。3、同时,JVM又是各大软件公司笔试、面试的重中之重,据统计,头部的30家互利网公司,均将JVM作为笔试面试的内容之一。4、JVM内容庞大、并且复杂难学,通过视频学习是最快速的学习手段。课程介绍本课程包含11个大章节,总计102课时,无论是笔试、面试,还是日常工作,可以让您游刃有余。第1章 基础入门,从JVM是什么开始讲起,理解JDK、JRE、JVM的关系,java的编译流程和执行流程,让您轻松入门。第2章 字节码文件,深入剖析字节码文件的全部组成结构,以及javap和jbe可视化反解析工具的使用。第3章 类的加载、解释、编译,本章节带你深入理解类加载器的分类、范围、双亲委托策略,自己手写类加载器,理解字节码解释器、即时编译器、混合模式、热点代码检测、分层编译等核心知识。第4章 内存模型,本章节涵盖JVM内存模型的全部内容,程序计数器、虚拟机栈、本地方法栈、方法区、永久代、元空间等全部内容。第5章 对象模型,本章节带你深入理解对象的创建过程、内存分配的方法、让你不再稀里糊涂。第6章 GC基础,本章节是垃圾回收的入门章节,带你了解GC回收的标准是什么,什么是可达性分析、安全点、安全区,四种引用类型的使用和区别等等。第7章 GC算法收集器,本章节是垃圾回收的重点,掌握各种垃圾回收算法,分代收集策略,7种垃圾回收器的原理和使用,垃圾回收器的组合及分代收集等。第8章 GC日志详解,各种垃圾回收器的日志都是不同的,怎么样读懂各种垃圾回收日志就是本章节的内容。第9章 性能监控与故障排除,本章节实战学习jcmd、jmx、jconsul、jvisualvm、JMC、jps、jstatd、jmap、jstack、jinfo、jprofile、jhat总计12种性能监控和故障排查工具的使用。第10章 阿里巴巴Arthas在线诊断工具,这是一个特别小惊喜,教您怎样使用当前最火热的arthas调优工具,在线诊断各种JVM问题。第11章 故障排除,本章会使用实际案例讲解单点故障、高并发和垃圾回收导致的CPU过高的问题,怎样排查和解决它们。课程资料课程附带配套项目源码2个159页高清PDF理论篇课件1份89页高清PDF实战篇课件1份Unsafe源码PDF课件1份class_stats字段说明PDF文件1份jcmd Thread.print解析说明文件1份JProfiler内存工具说明文件1份字节码可视化解析工具1份GC日志可视化工具1份命令行工具cmder 1份学习方法理论篇部分推荐每天学习2课时,可以在公交地铁上用手机进行学习。实战篇部分推荐对照视频,使用配套源码,一边练习一遍学习。课程内容较多,不要一次性学太多,而是要循序渐进,坚持学习。      
Java虚拟机(JVM)是Java程序的运行环境,它负责将Java字节码转换为可执行的机器码并执行。深入理解Java虚拟机涉及了解JVM的工作原理、内存管理、垃圾回收、类加载机制等方面的知识。 首先,JVM的工作原理是通过解释器或即时编译器将Java字节码转换为机器码并执行。解释器逐条执行字节码指令,而即时编译器将字节码转换为本地机器码,以提高程序的执行效率。 其次,内存管理是JVM的重要任务之一。JVM将内存分为不同的区域,包括堆、栈、方法区等。堆用于存储对象实例,栈用于存储局部变量和方法调用信息,方法区用于存储类的信息。JVM通过垃圾回收机制自动回收不再使用的对象,释放内存空间。 此外,类加载机制也是深入理解JVM的关键内容之一。类加载是将类的字节码加载到内存中,并进行验证、准备、解析等操作。类加载器负责查找并加载类的字节码,而类加载器之间存在着父子关系,形成了类加载器层次结构。 还有其他一些与性能优化、调优相关的内容,如即时编译器的优化技术、垃圾回收算法的选择等,也是深入理解Java虚拟机的重要方面。 总的来说,深入理解Java虚拟机需要对JVM的工作原理、内存管理、垃圾回收、类加载机制以及性能优化等方面有较深入的了解。掌握这些知识可以帮助开发人员编写出更高效、稳定的Java程序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值