《深入立即Java虚拟机》第二版-第3章读书笔记
分代收集算法
描述:
根据对象存活周期的不同将内存划分为几块(一般为新生代和老年代),这样可以根据各个年代的特点采用最适当的收集算法。
新生代
每次垃圾收集时都有大批对象死去,只有少量存活。
适用算法:
复制算法
复制算法
描述:
为了解决标记清除算法的效率问题而出现的算法。
它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。
优点:
这样使得每次都是对整个半区进行内存回收,内存分配时也就不用考虑内存碎片等情况,只要移动堆顶指针,按顺序分配内存即可,实现简单,运行高效。
缺点:
1.将内存缩小为原来的一半。
2.在对象存活率较高时就要进行较多的复制操作,效率就会变低。
具体实施:
新生代中又分为一块较大的Eden和两块较小的Survivor空间(HotSpot默认大小比例为8:1),每次使用Eden和一块Survivor。当回收时,将Eden和其中一块Survivor中存活的对象复制到另外一块Survivor空间上,最后清理使用的两块空间。
老年代
对象存活率高、而且没有额外空间对它进行分配担保。
适用算法:
标记清除算法、标记整理算法
标记清除算法
描述:
算法分为“标记”和“清除”两个阶段:首先标记处所有需要回收的对象,在标记完成后统一回收所有被标记的对象。
该算法是最基础的收集算法,下面的收集算法都是基于这种思路并对其不足进行改进而得到的。
缺点:
1.效率低,标记和清除两个过程的效率都不高
2.标记清除之后会产生大量的不连续的内存碎片,空间碎片太多可能导致以后在程序运行过程中需要分配较大对象时,无法找到足够的连续内存而不得不提前出发另一次垃圾收集动作。
标记整理算法
描述:
根据老年代特点(存活率较高),而提出的一种算法。首先标记需要回收的对象,然后让所有存活的对象都向一端移动,最后直接清理掉端边界以外的内存。
分配策略(创建对象时,对象处于哪一代,以及怎么换代):
对象优先分配在新生代(Eden)
大多数情况下,对象在新生代Eden区中分配。当Eden区没有足够空间进行分配时,虚拟机将发起一次Minor GC。
大对象直接进入老年代
大对象指需要大量连续内存空间的Java对象(最典型的就是很长的字符串以及数组[])。经常出现大对象时,容易导致内存还有不少空间时就提前触发垃圾收集以获取足够的连续空间给它们(特别应该避免朝生夕灭的大对象)。
长期存活的对象将进入老年代
虚拟机给每个对象定义了一个对象年龄计数器。如果对象在Eden出生并经过第一次Minor GC后存活,并且能被Survivor容纳的话,将被移动到Survivor空间中,并且对象年龄设为1。在Survivor中,每存活过一次Minor GC,年龄就增加1岁,当年龄增加到一定程度(默认15),就会转移到老年代中。
动态对象年龄判定
虚拟机不是永远地要求对象年龄必须达到年龄阈值才能到达老年代。如果Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以进入老年代。
空间分配担保:
描述:
在发生Minor GC之前,虚拟机会先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果条件成立,那么Minor GC可以确保是安全的(即使Eden在Minor GC后依然不能容纳该对象,但是老年代可容纳该对象,所以安全)。
如果不行,则虚拟机会查看HandlePromotionFailure设置值是否允许担保失败,如果允许,就进行Minor GC;否则,改为进行Full GC。
作用:
为了避免频繁的Full GC。
不同种类的GC
Minor GC(新生代GC):
描述:
指发生在新生代的垃圾收集动作
特点:
由于Java对象大多都具有朝生夕死的特性,所以Minor GC非常频繁,一般回收速度也比较快。
Major GC/Full GC(老年代GC):
描述:
发生在老年代的垃圾收集动作
特点:
速度较慢,一般比MinorGC慢10倍以上