天天看点

JVM垃圾回收与内存分配

JVM垃圾回收与内存分配

垃圾回收判断算法:

1. 引用计数法

给对象添加一个计数器,当引用时计数器就加1,如果引用失效时,则计数器则减1。如果计数器为0时,则表示该对象不被引用。从而判定可以回收。

2. 可达性分析算法

GCROOTS为起点,向下搜索,搜索过程的路径作为引用链,当一个对象没有任何一个引用链,则表明对象不可被引用。
GCRoot的对象:

2.1 虚拟机栈中引用的对象

2.2 方法区中的类静态属性引用对象

2.3 方法区中常量引用对象

2.4 本地方法栈中JNI引用的对象

引用:

引用可以分为:强引用、软引用、弱引用、虚引用
						强引用:一定不会被回收的对象、发生泄露的主要原因是不可回收对象
						软引用:如果下次GC时,内存不够才回收、内存够则不回收
						弱引用:在下次GC时一定会被回收。
						虚引用:跟踪对象被垃圾回收的状态。
           

垃圾回收的特殊情况

若被系统标记为回收的对象,在GC之前调用了finalize()。则可以逃脱一次GC。如果调用之后,仍然被标记,那么下次一定被回收。也就是说finalize()方法只能实现一次。

垃圾回收算法

1.标记-清除算法

首先要标记回收的对象,标记完成之后统一回收。在垃圾回收时会产生大量的碎片

2.标记-整理算法

首先标记回收的对象,标记完成后,将存存活的对象移动到另一端,然后清除掉端界以外的对象。

3.复制算法

将内存分为两块区域,每次只用其中的一块,当一块内存用完时,会将存活的对象移动到另一块内存中,一般用来回收新生代。

HotShot算法的实现

1.枚举根节点:可达性分析算法从GCROOTS节点中寻找引用链,此时整个虚拟机都需要停止工作,因为防止引用链不停的变化。CMS收集器需要stw。目前主流的虚拟机都是准确式GC。实现的方法都是OopMap。在jit编译期,OopMap会在特定的位置记录下引用的位置。

2.安全点:在枚举时,OopMap会记录下引用位置的安全点,只要到安全点才能暂停,此时一般分为抢先式中断和主动式中断。抢先式中断在GC时,会把所有线程都中断,让没有在安全点的线程恢复跑到安全点。主动式中断,如果发现线程自己所经过的点为安全点,就会停止线程等待检测。

3.安全区:安全区域就在一段代码中,引用不会发生变化。如需要检测,则会等待检测,如果检测完毕后方可离开安全区域。

垃圾回收步骤

如果采用可达性分析算法,JVM虚拟机会在当前程序设置GCROOTS节点,此时会向下搜索,其中会由OopMap帮助在安全点进行标记引用,然后在安全区进行检测。如果有引用失效的情况,则会进行垃圾收集。在不同的区域中,采用不同的收集器。

垃圾收集器

1.Serial收集器:只使用一个CPU的收集器,在垃圾收集时会中断其他工作线程,优点是简单高效,依然是Client默认下的新生代收集器。

2.parnew 收集器:是Serial收集器的多线程版本。只有该收集器可以和CMS收集器配合,是一款并发收集器。

3.parnew scavenge 收集器: 是新生代收集器,采用的是复制算法,主要控制吞吐量

4.Serial Old收集器,是Serial收集器的老年代版本。单线程收集器,可以和parnew scavenge收集器共用。

5.parnew old 收集器,是parnew 收集器的老年代版本,吞吐量优先。

6.CMS收集器,吞吐量优先的收集器,响应时间端。用于B/S系统的服务端,采用标记-清除的算法。共有4个阶段。分别为:初始标记、并发标记、重新标记、并发清除。其中初始标记和重新标记需要stw。初始标记时间较短,只是GCRORTS关联一下对象。重新标记是修正并发标记期间因用户程序继续运作而导致的变动。耗时时间较长的是并发标记和并发清除。但是会随着用户一起工作。缺点是:标记-清除算法。会产生大量的内存碎片。还会产生浮动垃圾,主要是因为并发标记产生的。

7.G1收集器:面向服务端的垃圾回收器。并行与并发、分代收集、空间整合、可预测停顿。执行步骤为、初始标记、并发标记、最终回收、筛选回收。

内存分配与回收策略

1.优先分配到Eden区,没有足够的空间会发生minor gc,此时from survivor 会移动到to survivor

2.如果新生代gc完成后,内存还不够则大对象会直接分配到老年代,主要是内存担保机制。此外长期存活的对象也会进入到老年代。系统默认为新对象的年龄为1,当经历过一次gc,年龄则会加1,当年龄增加到15时,则会移入到老年代。

3.在新生代发生的gc 为minor gc,老年代发生的gc 为major gc 。而full gc则表明同时发生在老年代和新生代。

回收机制

首先内存分配到Eden区中 ,如果Eden区的内存不够则会发生minor gc,此时存活的对象会从from survivor移动到to survivor区,eden区的对象也会移入并发生垃圾回收。这样不会产生内存碎片。当to survivor区内存满时,发生gc 会将内存移入到另一个survivor区,并发生垃圾回收,如果经历了15次gc 则会移动到老年代。新生代默认采用的serial收集器。或者采用parnew scavenge收集器。如果接着内存分配,长期存活的对象会移入到老年代中。如果老年代内存不足时,则会发生full gc。目前来说,垃圾回收都是分代收集的。采用cms收集器。因为响应时间短。并且为并发的操作。cms和parnew收集器共同工作。