在Java中,除了整型和引用这样的基本类型,所有对象都被分配在堆区而不是栈区。这种设计使得程序员不需要关注变量的生命周期,但代价是产生更多的垃圾。
可达性
对任何指针解引用就可以被程序直接访问的数据则为可达的。
局部性原理
如果一个程序方位的存储位置很可能将在一个很短的时间段再次被访问,则称这个程序具有时间局部性(Temporal locality)。如果被访问过的存储位置的临近位置很可能在一个很短的时间段内被访问,则该程序具有空间局部性。
通常认为程序把90%的时间来执行10%的代码。
几种垃圾回收器的原理
标记-清除收集器
这种收集器首先遍历对象图并标记可到达的对象,然后扫描堆栈以寻找未标记对象并释放它们的内存。这种收集器一般使用单线程工作并停止其他操作。并且,由于它只是清除了那些未标记的对象,而并没有对标记对象进行压缩,导致会产生大量内存碎片,从而浪费内存。
标记-压缩收集器
有时也叫标记-清除-压缩收集器,与标记-清除收集器有相同的标记阶段。在第二阶段,则把标记对象复制到堆栈的新域中以便压缩堆栈。这种收集器也停止其他操作。
复制收集器
这种收集器将堆栈分为两个域,常称为半空间。每次仅使用一半的空间,JVM生成的新对象则放在另一半空间中。GC运行时,它把可到达对象复制到另一半空间,从而压缩了堆栈。这种方法适用于短生存期的对象,持续复制长生存期的对象则导致效率降低。并且对于指定大小堆来说,需要两倍大小的内存,因为任何时候都只使用其中的一半。
增量收集器
增量收集器把堆栈分为多个域,每次仅从一个域收集垃圾,也可理解为把堆栈分成一小块一小块,每次仅对某一个块进行垃圾收集。这会造成较小的应用程序中断时间,使得用户一般不能觉察到垃圾收集器正在工作。
部分回收原理
通常80%~90%的新分配对象在几百万条指令之内或者再分配了。
分代收集器(Generational garbage coolection)
它是基于拷贝回收器和部分回收原理。
充分利用大多数对象“英年早逝”的特性的有效方法。
将堆区分成一系列小的区域,用0,1,2......n对它们进行编号,序号越小的区域存放的对象越年轻,对象首先在0区域被创建,填满后垃圾被回收,可达对象被移到1区,每一轮垃圾回收都是针对序号小于等于i的区域进行的,i为当前被填满区域的最高编号。
只要对i进行回收,所有序号小雨i的区域也将要进行垃圾回收,应为较年轻的世代往往包含了较多的垃圾,也就是更频繁的被回收。
最老的世代保存了最成熟的对象,对这些对象的回收是最昂贵的,相当于一次完整的回收。可引起较长时间的停顿。解决方法是使用列车算法。
HotSpot的四种GC 回收器:
串行化回收serial collector:
特点:回收时会暂停应用.
young区域:将Eden和某个Survivor区域中的存活的对象复制到另一个Survivor区域(设为TO)(大对象直接放到old区域).如果TO区域满了,则直接复制到old区域.
old区域:使用mark-sweep-compact GC算法,也就是先标记存活对象,然后清除废弃对象,然后把存活对象都移到一块区域,空出一片较大的空闲空间.
适用范围:大部分客户端的应用都可以使用这种回收算法,这也是HotSpot默认的回收算法.在现在的机器(06年)上一个64MB的区域的一次完全回收所需的时间不到半秒钟.
并行回收parallel collector:
特点:可以利用多核的CPU.
young区域:同样还是要暂停应用,基本机制和串行化差不多,不过是使用多线程.可以加快效率.
old区域:同串行化.
多核计算机上面可以使用.
并行压缩回收parallel compacting collector:
与并行回收相比,主要是在old区域有个新的算法,同时,按白皮书的说法,这种回收最终会替代并行回收.
young区域:同并行回收.
old区域:首先,把old分为几个连续的区域.然后,在每个区域并行的进行检查,标记出alive的对象(先标记出可以直接引用的对象,然后是所有的).然后开始对这些区域进行检查,得出密集程度(左边的区域肯定比右边的密集),从某个密集程度不很高的区域开始,并行的对右边区域进行压缩.
适应范围:对于多核,且对pause time有要求的环境下,使用并行压缩回收比并行回收要好.但是对于高共享率的服务器(也就说一台服务器运行多个应用),由于old区域的collection较慢,又是多线程,所以一个应用的GC会对其他应用造成影响.对应的解决方法:可以配置减少并行时的线程数目.
并行标记清除回收Concurrent Mark Sweep collector:
young区域:同并行回收.
old区域:分为几个步骤.
Initialmark:在需要执行GC时,先暂停应用,然后把所有直接引用到的对象进行标记.
Concurrentmark:然后继续应用,并同时对已标记对象进行检查,得到所有存活的对象.
remark:再次暂停应用,对Concurrent mark持续期间应用程序修改了的对象进行检查(新增的,废弃的),并标记存活对象.这个阶段持续时间较长,因此会使用多线程.在阶段结束后,所有的存活对象都被标记了,未标记的对象就是垃圾对象了.
sweep:停止暂停应用程序,然后把所有垃圾对象的空间释放.
与其他算法的不同点:
第一:不执行压缩.不过会通过计算将来可能的内存需求而合并/分割某些内存块.
第二:不是old区域要满了才执行GC,而是在空间小于一定程度时开始.
第三:由于没执行压缩,因此会产生碎片.
另外,CMS还可以使用增量运行方式,就是在Concurrentmark阶段只执行一部分工作,然后把资源还给应用程序.回收器的工作会分为几个部分并安排在两次young区域的回收空闲阶段完成.这种模式一般用在对暂停时间有要求,同时处理器数目不多的情况下(单核或双核).
总体说来,与并行回收相比,CMS降低了old GC的暂停时间(有时候效果很显著),轻微的加长了young GC的时间(因为对象从young区域转到old区域时间会加长:没执行压缩,因此要先找到合适的区域),降低了整个系统的一些执行效率,以及很大的加强了对于内存空间的需求.
Atas ialah kandungan terperinci 详解Java中的几种垃圾回收原理. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!