1. 垃圾回收器如何确定对象死亡
1.1 引用计数法
实现原理
:给对象中添加一个引用计数器,每当有引用它时,计数器就加1,如果引用失效,计数器值就减1,计数器为0的对象就是没有被再使用过的对象。
优点
:引用计数算法实现起来较为简单,判定效率也很高。
缺点
:无法解决互相循环引用
的问题。
1.2 可达性分析算法
实现原理
:通过一系列的称为GC Roots
的对象作为起始点,当一个对象到GC Roots
没有任何引用链(Reference Chain
)相连时,则表明此对象不可用
注意
:在Java语言中,可作为GC Roots
的对象包括下面几种:
虚拟机栈(虚拟机栈中的本地变量表)中引用的对象。
方法区中静态类属性引用的对象
方法区中常量引用的对象
本地方法栈中native
方法引用的对象
注意
:在可达性分析算法中,并不是不可达的对象一定会被回收,至少要经历两次标记
过程:
如果某个对象在进行可达性分析算法之后没有发现和
GC Roots
的引用链,它会被标记一次并且进行筛选
,筛选的条件就是该对象是否有必要执行finalize()
方法,当对象没有覆盖finalize()
方法,或者finalize()
方法已经被虚拟机调用过
,虚拟机在这两种情况下都会认为没必要执行
。
如果一个对象被判定为有必要执行finalize()
方法,那么这个对象会被放置在F-Queue
队列中,在稍后会由虚拟机建立一个,低优先级的Finalizer
线程去执行它,但不会等它结束
。
最后一次挣扎
finalize()
方法是对象逃脱死亡的最后一次机会,稍后GC
将会对F-Queue
中的对象进行第二次小规模的标记,如果对象要在finalize()
方法中进行自救
,只需要重新与引用链上任何一个对象建立关联即可。
2. 垃圾回收算法
2.1 标记-清除算法(Mark-Sweep)
顾名思义,分为标记
和清除
两个阶段:标记出所有需要回收的对象,然后统一回收所有被标记的对象。
缺点
:标记和清除的效率都不高,标记清除后会有大量不连续的内存碎片,而过多的内存碎片,会导致后续分配大对象时无法找到足够的连续内存空间,继而触发另一次垃圾回收动作
2.2 复制算法(Copying)
将可用内存分为大小相同两块。每次只用一块,当一块空间用完了,就将还存活的对象复制到另一块上,然后将刚使用过的内存空间一次清理掉。这样使得每次都是对其中的一块进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况。实现简单,运行高效。
优点
:算法实现简单,内存效率高,不易产生碎片。缺点
:可用内存被压缩到了原本的一半。且存活对象增多的话,Copying算法的效率会大大降低。
2.3 标记-整理(Mark-Compact)
复制收集算法在对象存活率高的时候就要执行较多的复制操作,效率将会变低。更关键的是,如果不想浪费50%的空间,就需要有额外的空间进行分配担保用于应付半区内存中所有对象都100%存活的极端情况,所以在老年代一般不能直接选用这种算法。
原文地址
https://blog.csdn.net/byhook/article/details/83046041
因此人们提出另外一种“标记-整理”(Mark-Compact)算法由于老年代中的对象生存周期都较长,有人提出“标记-整理”算法,标记过程和“标记-清理”一样,但在清除已死对象的同时会对存活对象进行整理,这样可以减少碎片空间。
2.4 分代收集算法
当前商业虚拟机的垃圾收集都是采用分代收集
(Generational Collecting)算法,这种算法并没有什么新的思想出现,只是根据对象不同的存活周期将内存划分为几块。一般是把Java堆分作``新生代和老年代
,这样就可以根据各个年代的特点采用最适当的收集算法。在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就用复制算法,只要少量复制成本就可以完成收集。而老年代中因为对象的存活率较高、周期长,就用标记-整理
或标记-清除
算法来回收。
新生代复制回收机制
:
将新生代区域分为Eden
,From Survivor
,To Survivor
,比例为8:1:1
实现原理
:
a. Eden
区域最大,对外提供堆内存,当Eden
区快满的时候,进行Minor GC
,把存活对象放入From Survivor
区域,然后清空Eden区域
,移动之后会标记相应的age
为1,这样就 完成了一次Minor GC
b. 由于Eden
区域被清空,对外提供堆内存,当Eden
区域又快满的时候,就会再次触发Minor GC
,将Eden
区域和From Survivor
区域中存活的对象复制到To Survivor
区域中,然后将Eden
以及From Survivor
区域清空,标记从Eden
复制到To Survivor
区域中的对象年龄设置为1,再将从From Survivor
区域中复制过去的对象年龄+1
,这样完成了第二次GC
c. 同上,当Eden
区域再次快满的时候,就会将Eden
区域以及To Survivor
区域中的存活对象,复制到From Survivor
区域中,标记从Eden
复制到From Survivor
区域中的对象年龄设置为1,再将从To Survivor
区域中复制过去的对象年龄+1
,这样完成了第三次GC
。
d. ......后面的步骤跟之前的b、c
步骤交替进行
e. 当一个存活对象的年龄熬过一定次数之后(默认为15次
),该对象就会被移动到老年代。
老年代-标记整理回收机制
:
老年代
一般存放的是存活时间较久的对象,所以每一次 GC
时,存活对象比较较大,也就是说每次只有少部分对象被回收。
因此,根据不同回收机制的特点,这里选择 存活对象多,垃圾少 的标记整理
回收机制,仅仅通过少量地移动对象就能清理垃圾,而且不存在内存碎片化。
3 垃圾收集器
如果说垃圾收集算法是内存回收的方法论,那么垃圾收集器就是内存回收的具体实现。
3.1 Serial收集器
缺点
:它在进行垃圾回收的时候,必须暂停所有的工作线程。
3.2 ParNew收集器
它是Serial收集器
的多线程版本,用于新生代内存回收,可以配合CMS使用
3.3 Parallel Scavenge收集器
它是一个新生代收集器,也是使用复制算法
的收集器,它的目标是达到一个可控制的吞吐量。
3.4 Serial Old收集器
他是Serial收集器
的老年代版本,使用的是标记整理
算法。
3.5 Parallel Old收集器
他是Parallel Scavenge收集器
老年代版本,使用的是标记整理
和多线程。
3.6 CMS收集器
它是一种以获取最短停顿时间为目标的收集器。基于标记-清除
算法实现的,整个过程如下:
初始标记(CMS initial mark)
并发标记(CMS concurent mark)
重新标记(CMS remark)
并发清除(CMS initial sweep)
其中初始标记
和重新标记
依然需要暂停工作线程。
优点:并发手机,停顿低。
原文地址
https://blog.csdn.net/byhook/article/details/83046041
参考:
《深入理解Java虚拟机》