Java对象的存活周期也满足二八定律,也就是说,大约有80%左右的对象生命周期很短,需要将他们及时的清理掉,而仅仅20%左右的对象存活周期比较长,进行垃圾回收的时候,就需要注意。
根据对象存活周期的规律,JVM将内存划分为了新生代和老年代,新生代对应存活周期短的对象,老年代对应存活周期比较长的对象。
新生代又划分为Eden区和两个大小相同的Survivor区,由from指针和to指针维护。两者之间的内存分配比率默认是随机分配,也可以通过参数固定。
新分配的对象都在Eden区,如果Eden区满了,就会触发Minor GC,存活下来的对象会被复制到from指针的Survivor区,然后from指针和to指针交换位置,保证to指针内存空间为空。
在Survivor区复制超过15次的对象会被提升到老年代,因为负责计数标志只有四位。或者当Survivor区内存使用率超过50%,也会将复制次数比较高的对象提升到老年代。
如果有老年代对象对新生代对象的引用,GC时为了避免全堆扫描,利用卡表计数解决这个问题。HotSpot虚拟机将对内存划分为512字节的表,利用一张卡表来记录这张表内是否有老年代想新生代的引用,有的话,将这张表标记为脏表。
在进行Minor GC的时候,就不用进行全堆扫描,只扫描卡表,监测到脏表的时候,将标志位清除。对新生代GC之后,会对存活的对象进行复制操作,这个时候,会再次设置该部分内存的脏卡标志。
这也会带来两个问题:
1. 无条件写屏障的性能开销。
这个开销相比与全堆扫描的性能开销来说,是可以接受的。
2. 高并发下虚共享带来的性能开销。
这个会先判断卡页的标志位是否已经标记过了,如果已经标记过了,就不再进行标示。
参考资料:
JVM之卡表(Card Table)

JVM将内存划分为新生代和老年代,新生代包含Eden区和两个Survivor区,通过MinorGC清理短生命周期对象。老年代存放长生命周期对象。卡表用于追踪跨代引用,提高GC效率。
983

被折叠的 条评论
为什么被折叠?



