深入浅出 JVM GC(1)
# 前言
初级 Java 程序员步入中级程序员的有一个无法绕过的阶段------GC(Garbage Collection)。作为 Java 程序员,说实话,很幸福,不用像 C 程序员那样,时刻关心着内存,就像网上有句名言------生活从来都不容易,只不过是有人替你负重前行!是的,GC 在替我们做这些脏活累活,GC 像让我们把精力都放在业务上,而不用每时每刻都在想着内存。现在,GC 也是每个语言的标准配置了。不然谁会去使用这个语言呢?
然而,作为一个合格的程序员,对底层的好奇是进步的动力,如果一个程序员失去了好奇心,那就可以说他在程序员这条道路上就结束了。
难道我们不好奇 GC 到底是怎么做的吗?接下来,我们就分析 GC 做了哪些事情。
实际上,GC 主要做3件事情:
- 哪些内存需要回收?
- 什么时候回收?
- 如何回收?
说到底,GC 就是做这3件事情,如果你能解决这3个问题,那么你也可以实现一个 GC。
那我们就一个一个问题来看看。
1. 哪些内存需要回收
还记得我们之前分享的关于 JVM 运行时数据区吗?有堆,有栈,有方法区(永久代),还有直接内存,还有 PC 寄存器。其中,GC 的主要战场就是堆,当然,方法区也是需要 GC 的。但重点还是堆。
我们知道,堆中内存是共享的,基本所有的对象都是在堆中创建。当一个对象不需要使用了,理论上我们就需要释放他所占用的内存。
问题来了,如何分辨一个对象不需要使用了呢?答案是:不可能被任何途径使用的对象。也就是说他没有了任何引用。我们知道,引用在栈中,实例在堆中,当一个实例没有了指向他的引用,我们认为,这个实例就需要清除并释放他所占用的内存了。
那么 GC 是如何实现的呢?一般而言有2种方法:
- 引用计数法(有缺陷,无法解决循环引用问题,JVM 没有采用)
- 可达性分析(解决了引用计数的缺陷,被 JVM 采用)
什么是引用计数法呢?
给对象中添加一个引用计数器,每当有一个地方引用他是,计数器值就加1;当引用失效时,计数器值就减1;任何时刻计数器为0的对象就是不可能在被使用的。
虽然乍看这个算法简单,效率也高,但有一个问题这个算法无法解决,就是循环引用。试想一下:A 对象引用了 B,B 对象也引用了 A,但 A 和 B 都不被别的地方使用,也就是说,实际上这两个对象是垃圾对象,但是由于他们互相持有引用,导致他们的引用计数器都不为0,因此系统无法判断是垃圾,也无法回收他们。
所以,在现在的 JVM 中,是没有使用这个算法的。我们知道就行。
引用计数法不行,那就再说说可达性分析算法。
这个算法的基本思想就是通过一系列的称为 “GC Roots” 的对象作为起始点,从这些节点开始向下搜索,所有所走过的路径称为引用链(Reference Chain),当一个对象到 GC Roots 没有任何引用链相连(也就是对象不可达)时,则证明此对象是不可用的。如下图所示,obj5 , obj6, obj7 虽然互相有关联,但是他们到 GC Roots 是不可达的,所以他们将会判定为是可回收的对象。
那么哪些对象可以作为 GC Roots 对象呢?
- 虚拟机栈(栈帧中的本地变量表)中引用的对象。
- 方法区中类静态属性引用的对象。
- 方法区中常量引用的对象。
- 本地方法栈中 JNI (即 native 方法)引用的对象。
2. 什么时候回收?
注意:即使是在可达性分析算法中不可达的对象,也并非是"非死不可的",这时候他们实际上是处于 “缓刑” 阶段。因为要真正宣告一个对象的死亡,至少需要经历两次标记过程:
> 如果对象在进行可达性分析后发现没有与 GC Roots 相连接的引用链,那他将会被第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行 finalize 方法。注意:当对象没有覆盖 finalize 方法,或者 finalize 方法已经被虚拟机调用过,虚拟机将这两种情况都视为 “没有必要执行”。也就是说,finalize 方法只会被执行一次。
如果这个对象被判定为有必要执行 finalize 方法,那么这个对象将会放置在一个叫做 F-Queue 的队列之中,并在稍后由一个虚拟机自动建立的,低优先级的 Finalizer 线程去执行它。注意:如果一个对象在 finalize 方法中运行缓慢,将会导致队列后的其他对象永远等待,严重时将会导致系统崩溃。
finalize 方法是对象逃脱死亡命运的最后一道关卡。稍后 GC 将对队列中的对象进行第二次规模的标记,如果对象要在 finalize 中 “拯救” 自己,只需要将自己关联到引用上即可,通常是 this。如果这个对象关联上了引用,那么在第二次标记的时候他将被移除出 “即将回收” 的集合;如果对象这时候还没有逃脱,那基本上就是真的被回收了。
这里需要注意的一点就是:一个对象如果重写了 finalize 方法,那么这个方法最多只会被执行一次。
建议:如非必要,不要重写该方法。可以使用 try-finally 代替,此方式更好,更及时。同时注意:在 Mysql 的 JDBC 驱动中,com.mysql.jdbc.ConnectionImpl 就实现了 finalize 方法,作用是:当一个 JDBC Connection 被回收时,需要进行连接的关闭,如果开发人员忘记了关闭,则在 finalize 方法中进行关闭。但是,由于其调用的不确定性,这不能单独作为可靠的资源回收手段。
到这里,我们知道了什么时候进行回收:如果一个对象重写了 finalize 方法且这个方法没有被 JVM 调用过,那么这个对象会被放入一个队列等待被一个低优先级的线程执行 finalize 方法,如果在这个方法中对象不能自救,则这个对象在第二次标记过程中就会被标记死亡,等待 GC 回收。
3. 如何回收?
如何回收,这个问题非常的大,涉及到各种垃圾回收算法,各种垃圾收集器。限于本篇的篇幅,楼主将不会在这篇文章里深入探讨,这里只会列出一些大纲,这些大纲将是后面文章的摘要,我们将在后面的文章中深入探讨如何回收。
那么,有哪些摘要呢?
3.1 垃圾回收算法
- 标记清除算法
- 复制算法
- 标记整理算法
- 分代收集算法(堆如何分代)
这些算法是 GC 的基础,所有 GC 的实现都是基于这些算法来清除无用对象,然后释放内存空间。我们将会在后面的文章一个一个讲解。
3.2 有哪些垃圾收集器
- Serial 串行收集器(只适用于堆内存256m 一下的 JVM )
- ParNew 并行收集器(Serial 收集器的多线程版本)
- Parallel Scavenge (PS 收集器,该收集器以吞吐量为主要目的,是1.8的默认 GC)
- CMS 收集器(该收集器全称 Concurrent Mark Sweep,是一种关注最短停顿时间的垃圾收集器)
- G1 收集器(JDK 9 的默认 GC)
3.3 有哪些GC
- Young GC(又称 YGC,minor GC,年轻代 GC)
- Old GC (老年代 GC,只有 CMS 才会单独回收 Old 区)
- Full GC(又称 major GC)
- Mixed GC(混合 GC,G1 收集器独有)
好,以上就是如何回收的大纲,我们将在后面的文章中慢慢讲解。
总结
这篇文章主要总结了什么是 GC ,以及 GC 的作用,GC 主要做了3件事情,哪些内存需要回收,什么时候回收,如何回收。我们知道了 GC 通过可达性分析知道了哪些内存需要回收,那什么时候回收呢?执行 finalize 方法后如果还没有复活,将被回收。第三个问题:如何回收呢?这个问题是一个大课题,我们只是列出了一些大纲,比如有哪些垃圾收集器,有哪些垃圾算法,有哪些 GC 过程。这些细节我们将在后面慢慢讲解,逐步深入。
深入浅出 JVM GC(1)的更多相关文章
- 深入浅出 JVM GC(3)
# 前言 在 深入浅出 JVM GC(2) 中,我们介绍了一些 GC 算法,GC 名词,同时也留下了一个问题,就是每个 GC 收集器的具体作用.有哪些 GC 收集器呢? Serial 串行收集器(只适 ...
- 深入浅出 JVM GC(2)
# 前言 在 深入浅出 JVM GC(1) 中,限于上篇文章的篇幅,我们留下了一个问题 : 如何回收? 这篇文章将重点讲述这个问题. 在上篇文章中,我们也列出了一些大纲,今天我们就按照那个大纲来逐个讲 ...
- 深入浅出 JVM GC(4)常用 GC 参数介绍
# 前言 从前面的3篇文章中,我们分析了5个垃圾收集器,还有一些 GC 的算法,那么,在 GC 调优中,我们肯定会先判断哪里出现的问题,然后再根据出现的问题进行调优,而调优的手段就是 JVM 提供给我 ...
- Java性能优化之JVM GC(垃圾回收机制)
Java的性能优化,整理出一篇文章,供以后温故知新. JVM GC(垃圾回收机制) 在学习Java GC 之前,我们需要记住一个单词:stop-the-world .它会在任何一种GC算法中发生.st ...
- 如何避免后台IO高负载造成的长时间JVM GC停顿(转)
译者著:其实本文的中心意思非常简单,没有耐心的读者建议直接拉到最后看结论部分,有兴趣的读者可以详细阅读一下. 原文发表于Linkedin Engineering,作者 Zhenyun Zhuang是L ...
- 【转载】Java性能优化之JVM GC(垃圾回收机制)
文章来源:https://zhuanlan.zhihu.com/p/25539690 Java的性能优化,整理出一篇文章,供以后温故知新. JVM GC(垃圾回收机制) 在学习Java GC 之前,我 ...
- JVM GC机制
垃圾收集主要是针对堆和方法区进行. 回收机制: 现在的JVM基本都使用分代回收机制,把堆中内存区域分为新生代,老年代. 新生代: Eden(80%) Survivor0(10%) Survivor1( ...
- JVM 自带性能监测调优工具 (jstack、jstat)及 JVM GC 调优
1. jstack:占用最多资源(CPU 内存)的Java代码 https://www.cnblogs.com/chengJAVA/p/5821218.html https://blog.csdn.n ...
- 理解JVM GC
理解JVM GC对于我们把控Java应用有很大的帮助.下面我从运维角度,把网上的JVM相关的资料整理如下,以加深对JVM GC的理解.如有错误的地方,请看官指正. JVM内存使用分类 JVM的内存分区 ...
随机推荐
- python+selenium—webdriver入门(二)
本文中主要介绍webdriver常见的对象定位方法: 一.对象定位的目的 二.常见的对象定位方法 一.对象定位的目的: 1.操作对象 2.获得对象的属性,如:对象的class属性.name属性等 3. ...
- .Net 常用插件及第三方库
.Net 常用插件及第三方库 一:第三方插件 1:基于响应式编程思想的oc 地址:https://github.com/ReactiveCocoa/ReactiveCocoa 2:hud提示框 地址: ...
- JS基础-数组的常用方法-冒泡排序
1.数组 1.关联数组 以数字作为元素下标的数组,就是索引数组. 以字符串作为元素下标的数组,就是关联数组. 2.js的关联数组 ex:在php中 $array=[& ...
- 团队-爬取豆瓣电影TOP250-开发环境搭建过程
从官网下载安装包(http://www.python.org). 安装Python 选择安装路径(我选的默认) 安装Pycharm 1.从官网下载安装包(https://www.jetbrains.c ...
- 【慕课网实战】一、以慕课网日志分析为例 进入大数据 Spark SQL 的世界
课程整套CDH相关的软件下载地址:http://archive.cloudera.com/cdh5/cdh/5/ cdh-5.7.0 生产或者测试环境选择对应CDH版本时,一定要采用尾号是一样的版本 ...
- valgrind内存检测工具
valgrind 那点事 ---------------------------------------内存检测工具 valgrind要使用此工具,可以使用--tool=memcheck 在Valgr ...
- modbus转乐鑫物联网平台上传工具
乐鑫平台推荐个人用户使用 界面比较简洁
- golang注意问题
关于slice 我们都知道slice是在通过参数传递的时候传递的是引用 slice的appen操作是有返回值的,并不改变原值 例如 b := [],,,} c:=append(b, ) // b 不变 ...
- Linux(以RHEL7为例)下添加工作区的方法|| The Way To Add Workspace On Linux
Linux(以RHEL7为例)下添加工作区的方法 The Way To Add Workspace On Linux 作者:钟凤山(子敬叔叔) 编写时间:2017年5月11日星期四 需求:有时候在使用 ...
- C#情怀与未来
C#情怀与未来,怨天尤人还是抓住机会,能否跟上dnc新时代浪潮? 经常看到有.NET圈子在讨论是否应该转其它语言 C#情怀是一方面,如果觉得C#未来没前途,光靠情怀是撑不住的, 建议对C#未来 ...