rooobot / architecture-training

Architecture training camp homework
0 stars 2 forks source link

架构师训练营-第九周练习:作业 #20

Open rooobot opened 4 years ago

rooobot commented 4 years ago

问题一

请简述JVM垃圾回收原理。

rooobot commented 4 years ago

既然是垃圾回收,那首先要明白,什么是垃圾回收?

垃圾回收是指将已经分配了,但却不再使用的内存回收回来,以便能够再次分配的操作。

那么,有哪些垃圾回收的算法呢?

具体的回收方式主要有以下三种:

Java对象的生命周期有一定的特点:大部分Java对象只存活一小段时间,则存活下来的对象则会存活很长一段时间。

基于这样的特点,于是就有了分代回收的思想:将堆空间划分为两代,分别为新生代和老年代。新生代用来存储新建的对象,当对象存活的时间够长时,则将其移动到老年代。

对新生代的垃圾回收我们称之为Minor GC,老年代被触发垃圾回收时,表示堆空间已经耗尽了,所以,Java虚拟机需要做一次全堆扫描,也就是Full GC

新生代又有进一步的划分:Eden区和Survivor区,其中Survivor区还会进一步划分为两个大小相同的区,分别为from区和to区,这里的to区始终是空的。

new对象时,会从Eden区中划出一块用来存储对象的内存,当Eden区的空间耗尽时,会触发Minor GC,存活下来的对象则会被送到Survivor区。Minor GC时,Eden区和from区中的对象会被复制到to区,然后交换fromto两个指针,这样就可以保证下一次Minor GC时,to指向的Survivor区还是空的。

Minor GC的过程中,虚拟机会记录Survivor区中的对象被来回复制的次数,当某个对象被来回复制超过一次的次数(默认为15次)时,则该对象会被晋升至老年代。

同时,如果单个Survivor区的占用比例超过了一定的百分比(默认为50%),那么复制次数较高的对象也会被晋升至老年代。

由此可以看出,这里使用的是标记-复制算法。理想的情况下,Eden区中的对象基本都死亡了,要复制的数据将非常少,因此采用这种标记-复制算法的效果也就非常的好。

同时,到这里可以看到Minor GC不需要对整个堆进行垃圾回收。

如果老年代中的对象引用了新生代的对象时,Minor GC会不会对老年代中的对象进行扫描呢?

答案是不会。

虚拟机通过卡表的方案来解决这个问题。卡表就是将整个堆划分为一个个大小为512字节的卡,并且维护一个卡表,用来存储每张卡的标识位,这个标识位代表对应的卡是否可能存在指向新生代对象的引用,如果可能存在,那么就认为这张卡是脏的。

这样,在Minor GC时,就不用扫描老年代中的对象了,只需要在卡表中寻找脏卡,然后将脏卡中的对象加入到Minor GCGC Roots中即可。当完成所有脏卡的扫描之后,虚拟机会将脏卡的标识位清零。

这样新生代对象的垃圾回收就解决了。

老年代触发垃圾回收时,说明堆内存不够用了,此时就会触发Full GC

最后,对于新生代和老年代都有不同的垃圾回收器可供选择,各自有不同的特点。

新生代垃圾回收器,都是采用标记-复制算法

老年代垃圾回收器: