JVM&GC
先回顾啥是JVM:

引用:
强引用(Strong Reference)
•默认的赋值语句可以生成一个强引用
•GC时不会被释放
软引用(Soft Reference)
•仅被java.lang.ref.SoftReference引用
•JVM内存不足时,会被释放(FGC)
弱引用(Weak Reference)
•仅被java.lang.ref.WeakReference引用
•GC时被释放
虚引用(Phantom Reference)
•仅被java.lang.ref.PhantomReference引用
•目的不是为了释放内存,而是为了跟踪内存释放,可用于替代finalize函数
工具:



1、什么是GC
GC,全称是 Garbage Collection (垃圾收集)或者 Garbage Collector(垃圾收集器)。
研究 GC 的主要原因是 GC 的过程会有 Stop The World(STW)的情况发生,即此时用户线程会停止工作,如果 STW 的时间过长,则应用的可用性、实时性等就下降的很厉害。
GC主要解决如下3个问题:
- 如何找到垃圾?
- 如何回收垃圾?
- 何时回收垃圾?
1.1如何找到垃圾?
所谓垃圾,指的是不再被使用(引用)的对象。Java 的对象都是在堆(Heap)上创建的,我们这里默认也只讨论堆。那么现在问题就变为如何判定一个对象是否还有被引用,思路主要有如下两种:
- 引用计数法,即在对象被引用时加1,去除引用时减1,如果引用值为0,即表明该对象可回收了。
- 可达性分析法,即通过遍历已知的存活对象(GC Roots)的引用链来标记出所有存活对象
方法1简单粗暴效率高,但准确度不行,尤其是面对互相引用的垃圾对象时无能为力。
方法2是目前常用的方法,这里有一个关键是 GC Roots,它是判定的源头,感兴趣的同学可以自己去研究下,这里就不展开讲了。

1.2 如何回收垃圾?
垃圾找到了,该怎么回收呢?看起来似乎是个很傻的问题。直接收起来扔掉不就好了?!对应到程序的操作,就是直接将这些对象占用的空间标记为空闲不就好了吗?那我们就来看一下这个基础的回收算法:标记-清除(Mark-Sweep)算法。
1.2.1 标记-清除 算法(Mark Sweep)
该算法很简单,使用通过可达性分析分析方法标记出垃圾,然后直接回收掉垃圾区域。它的一个显著问题是一段时间后,内存会出现大量碎片,导致虽然碎片总和很大,但无法满足一个大对象的内存申请,从而导致 OOM,而过多的内存碎片(需要类似链表的数据结构维护),也会导致标记和清除的操作成本高,效率低下,如下图所示:

1.2.2 复制算法(Copying)
为了解决上面算法的效率问题,有人提出了复制算法。它将可用内存一分为二,每次只用一块,当这一块内存不够用时,便触发 GC,将当前存活对象复制(Copy)到另一块上,以此往复。这种算法高效的原因在于分配内存时只需要将指针后移,不需要维护链表等。但它最大的问题是对内存的浪费,使用率只有 50%。
但这种算法在一种情况下会很高效:Java 对象的存活时间极短。据 IBM 研究,Java 对象高达 98% 是朝生夕死的,这也意味着每次 GC 可以回收大部分的内存,需要复制的数据量也很小,这样它的执行效率就会很高。

1.2.3 标记-整理算法(Mark Compact)
该算法解决了第1中算法的内存碎片问题,它会在回收阶段将所有内存做整理。但它的问题也在于增加了整理阶段,也就增加了 GC 的时间。
如下图所示:

1.2.4 分代收集算法(Generation Collection)
既然大部分 Java 对象是朝生夕死的,那么我们将内存按照 Java 生存时间分为 新生代(Young) 和 老年代(Old),前者存放短命僧,后者存放长寿佛,当然长寿佛也是由短命僧升级上来的。然后针对两者可以采用不同的回收算法,比如对于新生代采用复制算法会比较高效,而对老年代可以采用标记-清除或者标记-整理算法。这种算法也是最常用的。JVM Heap 分代后的划分一般如下所示,新生代一般会分为 Eden、Survivor0、Survivor1区,便于使用复制算法。

将内存分代后的 GC 过程一般类似下图所示:
![]()
- 对象一般都是先在
Eden区创建 - 当
Eden区满,触发 Young GC,此时将Eden中还存活的对象复制到S0中,并清空Eden区后继续为新的对象分配内存 - 当
Eden区再次满后,触发又一次的 Young GC,此时会将Eden和S0中存活的对象复制到S1中,然后清空Eden和S0后继续为新的对象分配内存 - 每经过一次 Young GC,存活下来的对象都会将自己存活次数加1,当达到一定次数后,会随着一次 Young GC 晋升到
Old区 Old区也会在合适的时机进行自己的 GC
1.2.5 常见的垃圾收集器
前面我们讲了众多的垃圾收集算法,那么其具体的实现就是垃圾收集器,也是我们实际使用中会具体用到的。现代的垃圾收集机制基本都是分代收集算法,而 Young与 Old区分别有不同的垃圾收集器,简单总结如下图:

从上图我们可以看到 Young与 Old区有不同的垃圾收集器,实际使用时会搭配使用,也就是上图中两两连线的收集器是可以搭配使用的。这些垃圾收集器按照运行原理大概可以分为如下几类:
- Serial GC,串行,单线程的收集器,运行 GC 时需要停止所有的用户线程,且只有一个 GC 线程
- Parallel GC,并行,多线程的收集器,是 Serial 的多线程版,运行时也需要停止所有用户线程,但同时运行多个 GC 线程,所以效率高一些
- Concurrent GC,并发,多线程收集器,GC 分多阶段执行,部分阶段允许用户线程与 GC 线程同时运行,这也就是并发的意思,大家要和并行做一个区分。
- 其他
我们下面简单看一下他们的运行机制。
1.2.5.1 Serial GC
该类 Young区的为 Serial GC,Old区的为Serial Old GC。执行大致如下所示:

1.2.5.2 Parallel GC
该类Young 区的有 ParNew和 Parallel Scavenge,Old 区的有Parallel Old。其运行机制如下,相比 Serial GC ,其最大特点在于 GC 线程是并行的,效率高很多:

1.2.5.3 Concurrent Mark-Sweep GC
该类目前只是针对 Old 区,最常见就是CMS GC,它的执行分为多个阶段,只有部分阶段需要停止用户进程,这里不详细介绍了,感兴趣可以去找相关文章来看,大体执行如下:

1.2.5.4 其他
目前最新的 GC 有G1GC和ZGC,其运行机制与上述均不相同,虽然他们也是分代收集算法,但会把 Heap 分成多个 region 来做处理,这里不展开讲。

参考资料
- Java Hotspot G1 GC的一些关键技术(https://mp.weixin.qq.com/s/4ufdCXCwO56WAJnzng_-ow)
- Understanding Java Garbage Collection(https://www.cubrid.org/blog/understanding-java-garbage-collection)
- 《深入理解Java虚拟机:JVM高级特性与最佳实践》
JVM&GC的更多相关文章
- 如何避免后台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 GC(3)
# 前言 在 深入浅出 JVM GC(2) 中,我们介绍了一些 GC 算法,GC 名词,同时也留下了一个问题,就是每个 GC 收集器的具体作用.有哪些 GC 收集器呢? Serial 串行收集器(只适 ...
- 深入浅出 JVM GC(2)
# 前言 在 深入浅出 JVM GC(1) 中,限于上篇文章的篇幅,我们留下了一个问题 : 如何回收? 这篇文章将重点讲述这个问题. 在上篇文章中,我们也列出了一些大纲,今天我们就按照那个大纲来逐个讲 ...
- 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的内存分区 ...
- Java性能优化之JVM GC(垃圾回收机制)
Java的性能优化,整理出一篇文章,供以后温故知新. JVM GC(垃圾回收机制) 在学习Java GC 之前,我们需要记住一个单词:stop-the-world .它会在任何一种GC算法中发生.st ...
- JVM gc介绍
Java语言出来之前,大家都在拼命的写C或者C++的程序,而此时存在一个很大的矛盾,C++等语言创建对象要不断的去开辟空间,不用的时候有需要不断的去释放控件,既要写构造函数,又要写析构函数,很多时候都 ...
- 一夜搞懂 | JVM GC&内存分配
前言 本文已经收录到我的Github个人博客,欢迎大佬们光临寒舍: 我的GIthub博客 学习导图 一.为什么要学习GC&内存分配? 时代发展到现在,如今的内存动态分配与内存回收技术已经相当成 ...
随机推荐
- jquery设置、判断、获取input单选标签选中状态
1.设置某项单选input为选中状态: $("input[type='radio']").eq(1).attr('checked',true); ②也可设其属性checked为'c ...
- codeforces 582A GCD Table
题意简述: 给定一个长度为$n$的序列 将这个序列里的数两两求$gcd$得到$n^2$个数 将这$n^2$个数打乱顺序给出 求原序列的一种可能的情况 ------------------------- ...
- 汇编指令ADD
格式: ADD OPRD1,OPRD2 功能: 两数相加(不带进位) 例子: add ax,bx add ax,ax 解释:
- 《图解设计模式》读书笔记6-1 VISITOR模式
目录 1. Visitor模式简介 2. 示例 2.1 类图 2.2 代码 3. 模式的角色和类图 4. 思路拓展 4.1 双重分发 4.2 开闭原则 4.3 难以增加ConcreteElement角 ...
- 测开之路五十五:实现类似于unittest查找case
实现给一个路径,去查找test开头的测试用例文件 创建一个计算器的类,方便后面测试用 class Calculator(object): def add(self, x, y): return x + ...
- 关于Java中编码集的有趣现象和解释
这是在整理另一篇博客的时候发现的一个有趣的现象,是这样描述的:我们都知道Java默认使用的是UniCode编码集,我们也知道char类型占用两个字节.所以奇怪的现象又发生了(见代码): @Test p ...
- 购物车2.0版——python第6天
li = [{'}, {'}, {'}, {'}, {'}, ] shopping_car = {} # 定义购物车dict print('欢迎光临尚雅梦想旗舰店'.center(40)) # 先让顾 ...
- pyppeteer模块的基本使用
pyppeteer模块的基本使用 引言 Selenium 在被使用的时候有个麻烦事,就是环境的相关配置,得安装好相关浏览器,比如 Chrome.Firefox 等等,然后还要到官方网站去下载对应的驱动 ...
- Java并发Condition接口
java.util.concurrent.locks.Condition接口提供一个线程挂起执行的能力,直到给定的条件为真. Condition对象必须绑定到Lock,并使用newCondition( ...
- Cocos2d-x之UI控件简介
| 版权声明:本文为博主原创文章,未经博主允许不得转载. 在Cocos2d-x中提供了一种UI控件,UI控件是指用户交互界面的操作部件.UI控件可以是Button(按钮),CheckBox(选择框 ...