JVM学习笔记GC——JAVA语言的垃圾回收

Posted 九死九歌

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JVM学习笔记GC——JAVA语言的垃圾回收相关的知识,希望对你有一定的参考价值。

一、垃圾回收概述

1 什么是GC,为何要GC?


2 早期垃圾回收


3 Java的垃圾回收机制



二、垃圾回收的相关算法

1 垃圾回收算法的概述

  垃圾回收分为标记阶段和清除阶段,标记阶段找垃圾,清除阶段清掉找到的垃圾。

  标记阶段使用的算法有引用计数算法、可达性算法。

  清除阶段使用的算法有标记-清除算法、复制算法、标记-压缩算法。

2 标记阶段:引用计数算法


3 标记阶段:可达性分析算法





4 对象的finalization机制





  有趣的代码——对象复活记

public class Test 

	public static Test test;

	@Override
	protected void finalize() throws Throwable 
		super.finalize();
		System.out.println("finalizing...");
		test = this;
	

	public static void main(String[] args) throws InterruptedException 

		test = new Test();

		test = null;
		System.gc();
		Thread.sleep(500);

		if (test != null) 
			System.out.println("I am alive:)");
		 else 
			System.out.println("I am gg:(");
		

		test = null;
		System.gc();

		if (test != null) 
			System.out.println("I am alive:)");
		 else 
			System.out.println("I am gg:(");
		

	


5 清除阶段:标记 - 清除算法



6 清除阶段:复制算法



  我们很容易想到,幸存者区便使用的是复制算法。因为新生代死亡率高,导致复制算法的缺点不明显,而优势倍增。

7 清除阶段:标记 - 压缩算法




8 对比清除阶段三种算法

9 分代收集算法



10 增量收集算法


11 分区算法


三 、垃圾回收的相关概念

1 System.gc()


  我们来看一个有意思的代码:

public class Test 

	public static void main(String[] args) 
		new Test();
		System.gc();
//		System.runFinalization();
	

	@Override
	protected void finalize() throws Throwable 
		super.finalize();
		System.out.println("Override finalize");
	


  ·运行结果便是有可能执行析构函数也有可能没有执行析构函数,也就是没有进行gc,这也就说明System.gc()只是提醒虚拟机进行GC,但不一定就会马上GC。

  若将main函数第三行解除注释,便会执行fianlize方法,因为第三行调用的这个函数会强制执行引用对象的析构函数。

2 内存溢出与内存泄露

  内存溢出:


  内存泄漏:


  内存泄漏的举例:

3 STW - Stop The World



  另外,STW期间,多个垃圾回收线程可能是串行的,也可能是并行的。另外也有可能不进行STW,用户线程和垃圾回收线程一起运行。

4 安全点与安全区域


  安全点:

  安全区域:

5 JAVA中的各种引用



  强引用是哪怕OOM都不回收。

  软引用是要OOM才回收。

  弱引用是只要GC就回收。

6 强引用:永不回收


  强引用的特点:

7 软引用:内存不足才回收


  若内存足够,则不会回收软引用可触及对象。

// 创建强引用
ClassName strongRef = new ClassName(args);
// 创建弱引用
SoftReference<ClassName> softRef = new SoftReference<ClassName>(new ClassName(args));

8 弱引用:发现即回收



  只要一进行GC,管你是个啥都回收。

9 虚引用:对象回收跟踪

10 终结器引用

  最不常用的一种引用。

  了解即可,这不是重点。

四、垃圾回收器

1 垃圾回收器的分类





![其他分类方式![](https://img-blog.csdnimg.cn/31933fed451145689ecaa6b052032d28.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Lmd5q275Lmd5q2M,size_20,color_FFFFFF,t_70,g_se,x_16)

2 垃圾回收器的性能指标


  下图中的这三项指的是上图中标红的三项指标。




3 垃圾回收器的发展迭代史



  七款经典垃圾回收器

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



  对上图中彩色虚线的说明。

5 如何查看默认的垃圾回收器

6 Serial与Serial Old回收器:串行回收




7 ParNew回收器:并行回收


  下图是Serial Old和ParNew组合式GC


8 Parallel和Parallel Old回收器:吞吐量优先





  相关参数的设置:


9 CMS的概述与工作原理



  CMS的工作原理:



10 CMS的特点与弊端分析





  CMS的优势与弊端:

11 CMS的参数设置


12 认识G1垃圾回收器




13 G1的优势和不足

  ① 并行与并发 ② 分代收集
  ③ 空间整合

  ④ 可预测的停顿时间模型

  G1的缺点:

14 G1的参数设置


  G1的性能调优:

15 G1在生产环境的适用场景

16 分区(region)的使用介绍

  分区有点像分页哦。


17 G1的主要回收环节




  Remembered Set记忆集:用来回收跨代回收问题。


  每个对象的成员变量固然会记录他们引用了谁,而每个记忆集则记录了这个对象被哪个对象所引用。当然若两个对象来自同一个region则无需在记忆集中记录。

18 G1垃圾回收过程的详细说明

  过程一:新生代GC



  过程二:并发标记过程

  过程三:混合回收


  过程四:Full GC

19 G1垃圾回收的优化建议


20 七大经典垃圾回收器的总结


21 常用的GC日志参数的设置

22 GC日志中垃圾回收数据的分析





  MinorGC日志:

  FullGC日志:

  将VM options填入并运行如下代码:

/**
 * <b>VM options:</b><br/>
 * <code>	-Xms20M</code><br/>
 * <code>	-Xmx20M</code><br/>
 * <code>	-Xmn10M</code><br/>
 * <code>	-XX:+PrintGCDetails</code><br/>
 * <code>	-XX:SurvivorRatio=8</code><br/>
 * <code>	-XX:+UseSerialGC</code><br/>
 */
public class Test 

	public static final int _1M = 1024;

	public static void main(String[] args) 
		byte[] a1 = new byte[2 * _1M];
		byte[] a2 = new byte[2 * _1M];
		byte[] a3 = new byte[2 * _1M];
		byte[] a4 = new byte[4 * _1M];
	


  先在jdk7环境下运行,输出如下:

[GC (Allocation Failure) [DefNew: 8158K->603K(9216K), 0.0148287 secs] 8158K->6747K(19456K), 0.0162353 secs] [Times: user=0.01 sys=0.00, real=0.02 secs] 
Heap
 def new generation   total 9216K, used 4865K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  eden space 8192K,  52% used [0x00000000fec00000, 0x00000000ff029778, 0x00000000ff400000)
  from space 1024K,  58% used [0x00000000ff500000, 0x00000000ff596ce0, 0x00000000ff600000)
  to   space 1024K,   0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
 tenured generation   total 10240K, used 6144K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
   the space 10240K,  60% used [0x00000000ff600000, 0x00000000ffc00030, 0x00000000ffc00200, 0x0000000100000000)
 Metaspace       used 3183K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 343K, capacity 388K, committed 512K, reserved 1048576K

  首先注意第3行写着新生代内存大小是9216KB,即9MB,然而我们设置的新生代大小是10MB,这是因为这里只算了Eden和一个Survivor的大小。

  如图,前三个变量已经快要挤满了Eden,第四个变量进不来,然而Survivor他也进不去,于是新生代进行一次GC,三个2MB变量自然无法进入Survivor,于是进入Tenured。所以从log中我们也能看到老年代占用了6MB(那三个2MB),Eden则占用了4MB


  如果在jdk8中运行,结果如下:

  可见jdk8与jdk7垃圾回收过程存在不同。

23 新时代的Epsilon和Shenandoah


  jdk11中的一些新特性:Epsilon与ZGC

  jdk12中的Shenandoah:



24 革命性的ZGC


  吞吐量测试数据:

  延迟时间测试数据:


以上是关于JVM学习笔记GC——JAVA语言的垃圾回收的主要内容,如果未能解决你的问题,请参考以下文章

JAVA笔记之GC部分

Java GC的工作原理详解

JVM_GC了解(转发)

Java GC(垃圾回收)机制知识总结

JVM学习-java垃圾回收-GC日志

JVM的垃圾回收机制详解和调优