GC一个对对内存进行管理控制的进程,它会判断堆里的对象哪个正在被使用,哪些没有被使用,并适时地进行清理工作。在C中,对内存的分配和回收需要通过手动的方式进行,而Java则可以通过垃圾回收机制进行自动处理,这种机制的基本原理描述如下:
第一步.标记
这一步主要对内存里的对象进行判断,哪些是正在使用的,哪些是未使用的。并分别打上标记。
图中蓝色的是活动的对象,黄色的则是未被引用的非活动对象。但是要注意,如果要对内存空间中的所有对象进行判断并标记,那将是一个非常耗时的工作。
步骤二.常规的清理
直接删除非活动对象,把空间腾出来只剩下活动的对象。而Memory Allocator保存指向空白空间的指针。
步骤2a.带空间压缩的清理
通过把活动的对象分配到连续的内存空间上,提高内存分配效率。
改进——分代的垃圾回收机制
按照上述的方法进行垃圾回收会存在这样的弊端:随着内存中对象的数量越来越多,标记和清理、压缩的速率都会越来越低。而经验告诉我们,大多数的对象都只有一个很短的生命周期。
如上图所示就是一个统计图标,Y轴指的是内存对象的数量,Y轴指的是内存对象在内存中存在的时间。很显然大部分的对象都集中在很短的生命周期内。正因如此,工程师们设计了分代的垃圾回收机制。
首先,将JVM的堆内存空间分为三代:Young Generation、Old Generation、Permanent Generation
其中Young Generation是所有new出来的对象所存放的内存空间。当Young Generation中的空间被填满之后,会触发一个minor garbage collection进行内存清理。当对象的死亡率很高时,minor garbage collection的使用频率将会很高。将死亡的对象清理之后,剩余的活动对象将衰老一代,并在到达一定衰老程度之后进入old Generation。
在minor garbage collection进行垃圾清理的时候,会触发一个Stop the World Event,这会中断与当前gc相关的所有线程,直到清理结束。
Old Genetation的内存被填满之后同样会进行清理,叫做Major garbage collection,同样触发一个Stop the World Event,但是由于Major garbage collection会标记所有的对象,所以它触发的Stop the World Event将会比minor gargage collection触发的要长,如果一个应用被设计为快速相应的,那么应该尽可能地将Major garbage collection的空间缩小。
至于Permanent generation主要存储JVM用来解析类和方法的一些数据。
了解了为什么要进行分代,以及怎么分代,那么下面来了解下各代之间有什么联系。
Generation Garbage Collection的运行过程
首先,Younge Generation 由Eden和两个survivor space组成。
new出来一个对象时,首先将被存放在Eden中。如下图:
当把Eden的空间填满时,将会触发一个minor garbage collection,并将存活的对象转存至一个survivor space。如下图:
当Eden被再一次填满,触发第二次minor garbage collection时,同样的清理工作完成后Eden以及前一个suivivor space (s0)中的存活对象将被转存到s1。如下图:
并按照上述过程循环,由于大多数的对象的生命周期都很短,所以大多数的对象都在这三个区之间存亡。
但是,当一个对象的年龄到达old generation指定的临界值(例子中是8),对象就会被转入old Generation
最后当Old Generation的中内存空间也被填满时,就会触发一个Major garbage collection,把young generation 和 old generation中的所有对象进行一个标记,并执行清理工作。