Java中的GC机制

GC(Garbage Collection),java中的垃圾回收机制。

Java虚拟机中进行垃圾回收的场所有两个,一个是堆,一个是方法区。方法区通常被称为永久代,垃圾回收的频率较低,速度也较慢。(JDK1.8之后取消永久代改为Metaspace。元空间并不在虚拟机中,而是使用本地内存)

判断是否需要被回收

Java虚拟机采用可达性分析法来判断一个对象是否需要被回收。就是以一系列的称为“GC Roots"的对象作为起始点,从这些结点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。

GC Roots的对象包括:

  • 虚拟机栈中引用的对象;
  • 方法区中类静态属性引用的对象;
  • 方法区中常量引用的对象;
  • 本地方法栈JNI引用的对象;

Java虚拟机在进行死亡对象判定时,如果对象在进行可达性分析后没有与GC Roots相关联的引用链,则该对象会被JVM进行第一次标记并且判断是否执行finalize方法。

如果当前对象没有覆盖该方法,或者finalize方法已经被JVM调用过都会被虚拟机判定为“没有必要执行”,那么该对象将会被放置在一个叫做F-Queue的队列当中,由一个低优先级的Finalizer线程去执行它。

如果在finalize方法中该对象重新与引用链上的任何一个对象建立了关联,例如this关键字,那么该对象就会逃脱垃圾回收系统;如果该对象在finalize方法中没有与任何一个对象进行关联操作,那么该对象会被虚拟机进行第二次标记,该对象就会被垃圾回收系统回收。finaliza方法JVM系统只会自动调用一次,下一次回收,它的finalize方法不会被执行。(躲得了初一,躲不了十五??)

 

标记清除算法(Mark-Sweep):首先需要标记可以回收的对象内存,然后在对回收的内存进行清除。缺点是容易产生内存碎片,碎片太多可能会导致为大对象分配空间时无法找到足够的空间而提前触发GC。

 

复制算法(Copying):将内存分为两块,先只使用其中一块内存,GC时把存活对象全部复制到另一块内存上,第一块内存全部清空。复制算法可以解决碎片问题,但是缺点是会减少可用空间。复制算法如果存活对象多的话,效率会大大降低,所以它是用于新生代的回收算法。(新生代大部分对象都会被回收)

(算法图片来自https://www.cnblogs.com/dolphin0520/p/3783345.html)

 

标记整理算法(Mark-Compact):标记后不直接清理可回收内存,而是将存活对象都移动到一端,然后清除掉可回收内存。标记整理算法可以充分的利用空间,用于老年代的垃圾回收。(老年代的特点是存活对象较多,被清理的对象较少。在新生代躲过15次GC之后的对象会被转入老年代,大对象通常会被直接分配到老年代)

 

七种垃圾回收器:

 

Serial: 串行回收器,只使用一个线程进行垃圾回收,会暂停所有用户线程。对应JVM参数:-XX:+UseSerialGC,启用Serial(单线程复制)+Serial Old(单线程记整理)的收集器组合。

ParNew: Serial收集器在新生代的并行版本,也会暂停用户线程。对应JVM参数:-XX:+UseParNewGC,启用ParNew(多线程复制)+Serial Old(单线程标记整理)的收集器组合 ps:这个组合已经被"不推荐"了。

ParallelScavenge: 并行回收器,也叫吞吐量优先收集器,可配置GC线程数量。Serial收集器在新生代和老年代的并行版本,JAVA8的默认垃圾回收器。对应JVM参数:-XX:+UseParallelGC(互相激活),启用Parallel(多线程复制)+Parallell Old(多线程标记整理)的收集器组合。

ParallelOld: ParallelScavenge的老年代版本。对应JVM参数:-XX:+ParallelOldGC(与-XX:+UseParallelGC互相激活),启用Parallel(多线程复制)+Parallel Old(多线程标记整理)的收集器组合。

CMS : 并发标记清除回收器(并发指与用户进程同时进行),获取最短停顿时间的收集器,G1出现之前大型应用的首选收集器。对应JVM参数:-XX:+UseConcMarkSweepGC,启用ParNew(多线程复制)+CMS(并发标记清除)的收集器组合,Parallell Old做为老年代的备用 。

SerialOld: Serial收集器的老年代版本,做为老年代CMS的后备版本。已经被优化很少使用了。

G1:G1垃圾回收器,对应JVM参数:-XX:+UseC1GC,整体上采用标记整理算法,局部采用复制算法,不会产生内存碎片(CMS会产生碎片)。与应用程序并发执行,停顿时间大大减少且可以指定。Java9的默认垃圾收集器。

 

G1垃圾回收器的原理:将整个堆内存分成大小相同的子区域(Region),子区域没有代和区的限制,在JVM启动时会自动设置这些子区域的大小,也可以通过-XX;G1HeapRegionSize=n(1M~32M,且必须是2的幂),默认分为2048个分区(即最大32M*2048=64G内存)。G1仍属于分代收集,包含新生代的Region,仍会暂停所有线程,将存活的对象复制到老年代的Region。老年代的Region将一个区域复制到另一个区域完成清理。(还会用连续的Humongous区用来存放大对象,有时不得不启用FullGC)

G1回收器参数配置样例:-XX:+UseG1GC,-Xmx32g,-XX:MaxGCPauseMillis=100(最大GC停顿单位毫秒,一个软目标,JVM将尽量停顿小于这个时间)

 

四种引用类型:

强引用(就算出现了OOM也不会对该对象进行回收,默认是强引用)

软引用(内存不够的时候才会回收,使用SoftReference<T>实现)

弱引用(不管内存够不够用,GC都会回收,使 用WeakReference<T>实现)

虚引用(使用PhantomReference<T>实现,需要配合ReferenceQueue联合使用,发生gc后被放入到引用队列)

ps:虚引用就是用来监控某个对象的回收信息,在gc之后可以进行一个后置通知

ps:mybatis代码中缓存部分,大量使用了软引用和弱引用

 

posted @ 2019-05-08 23:08  有梦想的人不睡觉s  阅读(1916)  评论(0编辑  收藏  举报