全文共 1890 个字,读完大约须要 6 分钟。java
上一篇咱们讲了垃圾标记的一些实现细节和经典算法,而本文将系统的讲解一下垃圾回收的经典算法,和Hotspot虚拟机执行垃圾回收的一些实现细节,好比安全点和安全区域等。算法
由于各个平台的虚拟机操做内存的方法各不相同,且牵扯大量的程序实现细节,因此本文不会过多的讨论算法的具体实现,只会介绍几种算法思想及发展过程。安全
标记-清除算法是最基础的算法,像它的名字同样算法分为“标记”和“清除”两个阶段,首先须要标记出所须要回收的对象,标记完成后统一收集被标记的对象。数据结构
优势: 实现简单。 缺点: 产生不连续的内存碎片;“标记”和“清除”的执行效率都不高。性能
标记-清除算法执行过程图:学习
(本文图片来自《深刻理解Java虚拟机》)线程
复制算法就是将内存分为大小相同的两块,当这一块使用完了,就把当前存活的对象复制到另外一块,而后一次性清空当前区块。设计
优势: 执行效率高。3d
缺点: 空间利用率低, 由于复制算法每次只能使用一半的内存。指针
也称标记-压缩算法,标记-整理算法采用和标记清除算法同样的对象“标记”,但后续不会对可回收对象进行清理,而是将存活的对象往一端空闲空间移动,而后清理边界之外的内存空间。
优势: 解决了内存碎片问题,比复制算法空间利用率高。
缺点: 由于有局部对象移动,相对效率不高。
标记-整理算法执行过程图:
目前商用虚拟机都采用的是分代收集的算法,这种算法按照对象存活周期把内存分为几块,通常Java中分为新生代和老年代。把存活率低的对象分到新生代使用复制算法提升垃圾回收的性能,老年代则存放存活率搞的对象,使用标记-清除和标记-整理的算法,提升内存空间使用率。
新生代和老生代的具体介绍和参数配置,后续的文章会详细讲解。
本节将详细的介绍一下HotSpot虚拟机在执行垃圾回收时的一些细节,目的是让读者更好的理解Java虚拟机。
HotSpot虚拟机: 它是Sun JDK和OpenJDK自定的虚拟机,也是目前使用最普遍的虚拟机。
垃圾回收流程: Java虚拟机在内存回收以前,为了保证内存的一致性,必须先暂停程序的执行,也就是传说中的Stop The World(简称STW),在使用可达性分析算法枚举GC Roots,标记出死亡对象,再进行垃圾回收。
垃圾回收遇到的问题: 那既然是要暂停程序的运行,就必定要保证中止的时间足够短,而且可控,否则带来的灾难将是毁灭性的。
解决方案: 显然HotSpot在设计的时候也考虑到了这个问题,因此在JIT编译的时候就会使用OopMap数据结构来记录栈和寄存器上的引用,这样虚拟机就直接知道了那些地方存放着对象的引用,以下图,为我编译String.hashCode()方法的部分本地代码:
能够看出,使用OopMap数据结构存储了普通对象的指针引用。
查看汇编的方法,启动命令窗体执行:java -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly YouTestClass
命令可能会报错: Could not load hsdis-amd64.dll; library not loadable; PrintAssembly is disabled
报错解决方法:使用编译好的hsdis.dll放到:jre安装目录\bin\server目录下便可,hsdis.dll地址地址:https://pan.baidu.com/s/1-D6u0gnUx291LXS3bHOorA
安全点(Safepoint)
在OopMap的协助下,HotSpot能够快速的完成GC Roots枚举,但致使OopMap内容变化的指令不少,并且若是给每一个对象生成对应的OopMap,会形成大量额外的空间,这会致使GC成本很高,因此HotSpot只会在“特定的位置”生成对应的OopMap,这些位置就成为“安全点”。
HotSpot也并非任什么时候刻都会停顿下来进行GC,只会在程序都到底安全点以后才会GC,因此安全点的设置不能太少,让GC等待时间太长,也不能太多增大运行时的成本。
安全点的两种线程中断方式
抢断式中断:不须要线程的执行代码去主动配合,当发生GC时,先强制中断全部线程,而后若是发现某些线程未处于安全点,恢复程序运行,直到进入安全点为止。
主动式中断:不强制中断线程,只是简单地设置一个中断标记,各个线程在执行时轮询这个标记,一旦发现标记被改变(出现中断标记)时,那么将运行到安全点后本身中断挂起。目前全部商用虚拟机所有采用主动式中断。
安全区域(Saferegion)
安全点机制仅仅是保证了程序执行时不须要太长时间就能够进入一个安全点进行 GC 动做,可是当特殊状况时,好比线程休眠、线程阻塞等状态的状况下,显然HotSpot不可能一直等待被阻塞或休眠的线程正常唤醒执行;此时就引入了安全区的概念。
安全区(Saferegion):安全区域是指在一段区域内,对象引用关系等不会发生变化,在此区域内任意位置开始GC都是安全的;线程运行时,首先标记本身进入了安全区,而后在这段区域内,若是线程发生了阻塞、休眠等操做,HotSpot发起GC时将忽略这些处于安全区的线程。当线程再次被唤醒时,首先他会检查是否完成了GC Roots枚举(或这个GC过程),若是完成了就继续执行,不然将继续等待直到收到能够安全离开的Safe Region的信号为止。
《深刻理解Java虚拟机》
《垃圾回收的算法与实现》
关注公众号,发送“gc”关键字,领取《垃圾回收的算法与实现》学习资料。