vlambda博客
学习文章列表

JVM07:常见的几种垃圾回收器和引用类型

深入理解垃圾回收器

GC算法如引用计数算法、复制算法、标记清除算法、标记整理算法都是方法论,垃圾回收器就是这些算法对应的落地的实现。

四种垃圾回收器

1、串行垃圾回收器,单线程工作,执行GC时会停止所有的线程直到GC结束(STW:Stop the World)。其原理如下图所示。JVM07:常见的几种垃圾回收器和引用类型

2、并行垃圾回收器,多线程工作,也会导致STW。其原理如下图所示。JVM07:常见的几种垃圾回收器和引用类型

3、并发垃圾回收器,在回收垃圾的同时,可以正常执行线程,并行处理,但是如果是单核CPU,只能交替执行。其原理如下图所示。JVM07:常见的几种垃圾回收器和引用类型

4、G1垃圾回收器,将堆内存分割成不同的区域,然后并发的对其进行垃圾回收。Java9以后为默认的垃圾回收器。其原理如下图所示。JVM07:常见的几种垃圾回收器和引用类型

看默认的垃圾回收器:java -XX:+PrintCommandLineFlags -versionJVM07:常见的几种垃圾回收器和引用类型

Java的垃圾回收器有哪些?

Java曾经由7种垃圾回收器,现在有6种。主要垃圾回收器的位置分布和关系如下图所示。JVM07:常见的几种垃圾回收器和引用类型上图中,红色箭头表示新生区中使用了对应的垃圾回收器,在老年区只能使用对应箭头指向的垃圾回收器。蓝色箭头表示曾经的垃圾回收器有过的对应关系。

6种垃圾回收器名称分别是:

  • DefNew :默认的新一代 【Serial 串行】

  • Tenured :老年代 【Serial Old】

  • ParNew :并行新一代 【并行ParNew】

  • PSYoungGen :并行清除年轻代 【Parallel Scavcegn】

  • ParOldGen:并行老年区

    JVM07:常见的几种垃圾回收器和引用类型

JVM的Server/Client模式

现在的JVM默认都是Server模式,Client几乎不会使用。以前32位的Windows操作系统,默认都是Client的 JVM 模式,64位的默认都是 Server模式。

垃圾回收器之间的组合关系

上述6种垃圾回收器都是组合使用的,新生区使用了某种垃圾回收器,养老区会使用与之对应的垃圾回收器,并不是自由搭配的。如下图所示。JVM07:常见的几种垃圾回收器和引用类型

如何选择垃圾回收器

1、单核CPU,单机程序,内存小。选择-XX:UseSerialGC

2、多核CPU,吞吐量大,后台计算。选择XX:+UseParallelGC

3、多核CPU,不希望有时间停顿,能够快速响应。选择-XX:+UseParNewGC 或者 XX:+UseParallelGC

G1垃圾回收器

以往垃圾回收器的特点

1、年轻代和老年代是各自独立的内存区域。

2、年轻代使用Eden+s0+s1复制算法。

3、老年代垃圾收集必须扫描整个老年代的区域。

4、垃圾回收器原则:尽可能少而快的执行GC。

G1垃圾回收器的原理

G1(Garbage-First)垃圾回收器 ,是面向服务器端的应用的回收器。其原理如下图所示。原理:将堆中的内存区域打散,默认分成2048块。不同的区间可以并行处理垃圾,在GC过程中,幸存的对象会复制到另一个空闲分区中,由于都是以相等大小的分区为单位进行操作,因此G1天然就是一种压缩方案(局部压缩)。

使用G1垃圾回收器:-XX:+UseG1GC

G1垃圾回收器最大的亮点是可以自定义垃圾回收的时间。

设置最大的GC停顿时间(单位:毫秒):XX:MaxGCPauseMillis=100 ,JVM会尽可能的保证停顿小于这个时间。

G1垃圾回收器的优点

  • 没有内存碎片。

  • 可以精准的控制垃圾回收时间。

强引用、软引用,弱引用和虚引用

主要学习三个引用类:SoftReferenceWeakReferencePhantomReference

强引用

假设出现了异常或OOM,只要是强引用的对象,都不会被回收。强引用就是导致内存泄露的原因之一。

package com.wunian.ref;/** * 强引用 * -XX:+PrintGCDetails -Xms5m -Xmx5m */public class StrongRefDemo {
public static void main(String[] args) { Object o1=new Object();//这样定义的默认就是强引用 Object o2=o1; o1=null;
System.gc(); System.out.println(o1);//null System.out.println(o2);//java.lang.Object@6e0be858 }}

软引用

相对于强引用弱化了。如果系统内存充足,GC不会回收该对象,但是内存不足的情况下就会回收该对象。

package com.wunian.ref;import java.lang.ref.SoftReference;/** * 软引用 * -XX:+PrintGCDetails -Xms5m -Xmx5m */public class SoftRefDemo {
public static void main(String[] args) { Object o1=new Object();//这样定义的默认就是强引用 //Object o2=o1; SoftReference<Object> o2=new SoftReference<>(o1);//软引用 System.out.println(o1);//java.lang.Object@6e0be858 System.out.println(o2.get());//得到引用的值 java.lang.Object@6e0be858 o1=null; try { byte[] bytes=new byte[10*1024*1024]; } catch (Exception e) { e.printStackTrace(); } finally { System.out.println(o1);//null System.out.println(o2.get());//null //由于堆内存不足被回收 } //System.gc(); }}

弱引用

不论内存是否充足,只要是GC就会回收该对象。

package com.wunian.ref;import java.lang.ref.WeakReference;/** * 弱引用 * -XX:+PrintGCDetails -Xms5m -Xmx5m */public class WeakRefDemo {
public static void main(String[] args) { Object o1=new Object();//这样定义的默认就是强引用 WeakReference<Object> o2 = new WeakReference<>(o1);
System.out.println(o1);//java.lang.Object@6e0be858 System.out.println(o2.get());//得到引用的值 java.lang.Object@6e0be858
o1=null; System.gc();
System.out.println(o1);//null System.out.println(o2.get());//null }}

软引用、弱引用的使用场景

假设现在有一个应用,需要读取大量的本地图片。

1、如果每次读取图片都要从硬盘中读取,影响性能。

2、一次加载到内存中,可能造成内存溢出。

我们的思路:

1、使用一个HashMap保存图片的路径和内容。

2、内存足够,不清理。

3、内存不足,清理加载到内存中的数据。

虚引用

虚就是虚无,虚引用就是没有这个引用。虚引用需要结合队列使用,其主要作用是跟踪对象的垃圾回收状态。

package com.wunian.ref;import java.lang.ref.PhantomReference;import java.lang.ref.ReferenceQueue;import java.util.concurrent.TimeUnit;/** * 虚引用 */public class PhantomRefDemo {
public static void main(String[] args) throws InterruptedException { Object o1=new Object(); //虚引用需要结合队列使用 ReferenceQueue<Object> referenceQueue=new ReferenceQueue<>(); PhantomReference<Object> objectPhantomReference=new PhantomReference<>(o1,referenceQueue);
System.out.println(o1);//java.lang.Object@6e0be858 System.out.println(objectPhantomReference.get());//null System.out.println(referenceQueue.poll());//null
o1=null; System.gc(); TimeUnit.SECONDS.sleep(1);
System.out.println(o1);//null System.out.println(objectPhantomReference.get());//null //这好比是一个垃圾桶,通过队列来检测哪些对象被清理了,可以处理一些善后工作 System.out.println(referenceQueue.poll());//java.lang.ref.PhantomReference@61bbe9ba }}