深入理解Java虚拟机笔记---垃圾收集算法

当对象判定为"已死"状态,虚拟就要采取一定的手段将这些对象从内存中移除,即回收垃圾,回收过程有采用一定的算法。如下是一些主要的垃圾收集算法:

1.标记-清除算法

该算法是最基础的算法,分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收掉所有被标记的对象。之所有说它是最基础的算法是因为后续的收集算法都是基于这种思路并对其缺点进行改进得到的。它的缺点主要有两个:一个是效率问题,标记和清除过程效率都不高;另外一个是空间问题,标记清除后会产生大量不连线内存碎片,内存碎片太多导致当程序运行进需要分配较大对象时无法找到足够的连续内存而不得不提前触发另一次垃圾收集操作。标记-清除算法的执行过程如下图:

2.复制算法

为了解决效率问题,“复制”收集算法出现了,它将可用内存按容量分为大小相等的两块,每只使用其中一块。当这一块内存使用完了,就将还存活着的对象复制到另外一块上,然后再把已使用过的内存空间一次性清理空。这样使得每次都是对其中一块进行内存回收,内存分配时也不用考虑内存碎片的问题,只需要移动堆顶指针,按顺序分配内存即可,实现简单,运行高效。只是这种算法的代价是将内存缩小为原来的一半,代价太高了一点。复制算法执行过程如下图:

现在商业虚拟机都是采用这种算法来回收新生代,IBM的专门研究表明,新生代中的对象98%是朝生夕死的,生命周期很短,所以并不需要按照1:1的比例来划分内存空间,而是将内存分为一块较大的Eden空间与两块较小的Survivor空间,每次使用Eden与其中一块Survivor空间。当回收时,将Eden与Survivor中还存活的对象一次性地拷贝到另外一块Survivor空间中,最后清理掉Eden和刚才使用过的Survivor空间。HotSpot虚拟机默认Eden和Survivor空间比例为8:1,也就是每次新生代中可用内存为整个新生代容量的90%(80%+10%),只有10%的新生代内存是“浪费”的。当然,98%的对象可回收只是一般场景下的数据,但没有办法保证每回收都只有不多于10%对象存活,当Survivor空间不足时,需要依赖其它内存(老年代)进行分配担保。

3.标记-整理算法

复制算法在对象存活率较高时就要执行较多的复制操作,效率将会变低,如果不想浪费50%的空间,就需要有额外的空间进行担保,以应对被使用的内存中所有对象都100%存活的极端情况,所以在老年代一般不能直接选用这种算法。

根据老年代的特点,“标记-整理”算法被提出,标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清除,而是让所有存活对象都向一端移动,然后直接清除掉端边界以外的内存。“标记-整理”算法执行示意图如下:

4.分代收集算法

当前商业虚拟机的垃圾收集都采用“分代收集”(Generational Collection)算法,该算法将根据对象存活周期不同将内存划分为几块。一般把Java堆分为新生代与老年代,这样就可以根据各个年代的特点采用最适当的收集算法。在新生代中,每次垃圾收集都发现有大量对象死去,只有少量对象存活,就选得复制收集算法,只要付出少量存活对象的复制成本就可以完成收集。而老年代中因为对象存活率高、没有额外的空间对其进行分配担保,就必须使用“标记-清除”或“标记-整理”算法进行回收。

时间: 2024-10-12 00:07:36

深入理解Java虚拟机笔记---垃圾收集算法的相关文章

深入理解Java虚拟机笔记---垃圾收集器

如果说收集算法是内存回收的方法论,那么垃圾收集器就是内存回收的具体实现.Java虚拟机规范中对象垃圾收集器应该如何实现并没有任何规定,因此不同的厂商,不同版本的虚拟机所提供的收集器可能会有很的差别,并且一般会提供参数供用户根据自己的应用特点和要求组合出各个年代所使用的收集器.下面是Sun HotSpot虚拟机1.6版本Update22包含的所有收集器: 上图中,如果两个收集器之间存在连线,就说明它们可以搭配使用. 1.Serial收集器 Serial收集器是最基本.历史最悠久的收集器,曾经(在J

深入理解Java虚拟机之垃圾收集一

"生存还是死亡" 如何来判定对象是否存活?针对这个问题书中给出了两种算法,分别是引用计数算法和可达性分析算法 引用计数算法 该算法的思路简单并且易于实现.我们给对象中添加一个引用计数器,当有一个地方引用它时,引用计数器就加一,当引用失效时,计数器减一,当计数器为0时就说明该对象不可能再被引用. 客观的评价,该算法判定效率很高,在很多情况下都是一种不错的算法,但是,至少主流的Java虚拟机并没有采用采用这种算法.原因是该算法无法解决对象之间的循环引用问题. 什么是循环引用呢?笔者认为就是

深入理解java虚拟机笔记(一)-java内存区域与内存溢出

1. 前言 这是深入理解java虚拟机一书的笔记,来自第二章.因为这本书讲的比较深奥,这是第二次看,需要记录一下笔记. 2. 运行时数据区域 java虚拟机所管理的内存分为以下几个区域. ps:图片来自网络 2.1 程序计数器 程序计数器是一块较小的内存空间,他可以看做是当前线程所执行字节码的行号指示器.字节码解释器工作时就是通过改变这个计数器的值来选去下一条要执行的字节码指令,分之.循环.跳转.异常处理.线程恢复等基础功能都需要依赖这个计数器来完成. 这块内存是线程私有的内存. 如果线程在执行

深入理解Java虚拟机笔记---内存区域

Java虚拟机在执行Java程序过程中会把它所管理的内存划分为若干个不同的数据区域.这些区域有自各的用途,以及创建及销毁时间,有的区域随着虚拟机进程的启动而存在,有些区域则是依赖用户线程的启动和结束而建立和销毁.根据<Java虚拟机规范(第2版)>规定,Java虚拟机管理的内存区域包括以下几个运行时数据区域,下如图 1.程序计数器(Program Counter Register) 程序计数器是一块较小的内存空间,它的作用可以看做是当前线程所执行的字节码的行号指示器.字节码解释器工作时就是通过

《深入理解Java虚拟机》——垃圾收集器与内存分配策略

GC需要完成: 哪些内存需要回收 什么时候回收 如何回收 如何确定对象不再使用 引用计数算法 给对象添加一个引用计数器,当有一个地方引用它时,计数器值进行加1操作:当引用失效时,计数器值进行减1操作:当计数器值为0,则说明对象不可能再被使用.但是它无法解决循环引用的问题. public class ReferenceCountingGC { public Object instance = null; public static void testGC(){ ReferenceCountingG

深入理解java虚拟机(3)垃圾收集器与内存分配策略

一.根搜索算法: (1)定义:通过一系列名为"GC Roots"的对象作为起点,从这些起点开始向下搜索,搜索走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连的时候,则证明此对象不可用 (2)GC Roots对象包括这几种:虚拟机栈中引用的对象:方法区中的类静态属性引用的对象:方法区中常量的引用对象:本地方法中JNI的引用对象 二.垃圾收集算法 1.标记-清楚算法: (1)基本思想:首先标记出要回收的对象,在标记完后回收掉所有被标记的对象 (2)缺点:第一是标记和清楚

深入理解java虚拟机笔记(二)-垃圾回收

ps:文中的图片都来自网络.部分图片来源 1. 前言 作为一种高级语言,比起c和c++来,很进步的一点就是垃圾回收机制.这省去来了我们很多的工作,不过,我们仍然需要了解垃圾回收,这对我们的成长很有帮助. 2. 引用计数法 引用计数法在很多高级语言都有,如python,java也不例外.对象内部维护有一个被其他对象引用的引用计数,当这个引用计数为0的时候,表示对象可以被回收. 引用计数法存在一个问题,就是循环引用,加入a引用b,b同时也引用a,那么就存在ab的引用计数都不为0的情况. 3. 可达性

深入理解Java虚拟机--笔记1

Java内存区域与内存溢出异常 运行时数据区域 Java虚拟机在执行Java程序的过程中会把它所管理的内存区域划分为若干个不同的数据区域. 1 程序计数器--Program Counter Register 一块较小的内存空间,作用可视为当前线程所执行的字节码的行号指示器,没个线程有自己独立的程序计数器,即"线程私有"的内存区域. 若线程当前执行的是一个Java方法,则这个计数器记录的是当前执行的虚拟机字节码指令的地址: 若线程当前执行的是一个Native方法,则这个计数器值为空(Un

深入理解Java虚拟机笔记---属性表集合

在Class文件,字段表,方法表中都可以携带自己的属性表集合,以用于描述某些场景专有的信息.与Class文件中其它的数据项目要求的顺序.长度和内容不同,属性表集合的限制稍微宽松一些,不再要求各个属性表具有严格的顺序,并且只要不与已有的属性名重复,任何人实现的编译器都可以向属性表中写入自己定义的属性信息,Java虚拟机运行时会忽略掉它不认识的属性.为了能正确地解析Class文件,<Java虚拟机规范(第二版)>中预定义了9荐虚拟机实现应当能识别的属性,具体如下表所示: 对于每个属性,它的名称需要