JVM 调优系列之图解垃圾回收
摘要: jvm必知系列,总结一些常见jvm回收机制,方便查阅
对于调优之前,我们必须要了解其运行原理,java 的垃圾收集Garbage Collection 通常被称为“GC”,它诞生于1960年 MIT 的 Lisp 语言,经过半个多世纪,目前已经十分成熟了。因此本篇主要从这三个方面来了解:
1. 哪些对象需要被回收?
2. 什么时候回收?
3. 如何回收?
谁要被回收
java虚拟机在执行java程序的过程中会把它所管理的内存划分为若干个不同是数据区域,这些区域有各自各自的用途。主要包含以下几个部分组成:
程序计数器
程序计数器占用的内存空间我们可以忽略不计,它是每个线程所执行的字节码的行号指示器。
虚拟机栈
java的虚拟机栈是线程私有的,生命周期和线程相同。它描述的是方法执行的内存模型。同时用于存储局部变量、操作数栈、动态链接、方法出口等。
本地方法栈
本地方法栈,类似虚拟机栈,它调用的是是native方法。
堆
堆是jvm中管理内存中最大一块。它是被共享,存放对象实例。也被称为“gc堆”。垃圾回收的主要管理区域
方法区
方法区也是共享的内存区域。它主要存储已被虚拟机加载的类信息、常量、静态变量、即时编译器(jit)编译后的代码数据。
以上就是jvm在运行时期主要的内存组成,我们看到常见的内存使用不但存在于堆中,还会存在于其他区域,虽然堆的管理对程序的管理至关重要,但我们不能只局限于这一个区域,特别是当出现内存泄露的时候,我们除了要排查堆内存的情况,还得考虑虚拟机栈的以及方法区域的情况。
知道了要对谁以及那些区域进行内存管理,我还需要知道什么时候对这些区域进行垃圾回收。
什么时候回收
在垃圾回收之前,我们必须确定的一件事就是对象是否存活?这就牵扯到了判断对象是否存活的算法了。
引用计数算法
给对象中添加一个引用计数器,每当有一个地方引用它时,计数器+1,当引用失效,计数器-1.任何时刻计数器为0的对象就是不可能再被使用的。
优点:实现简单,判定效率高效,被actionscript3和python中广泛应用。
缺点:无法解决对象之间的相互引用问题。java没有采纳
可达性分析算法
通过一系列称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到GCRoots没有任何引用链相连的时候,则证明此对象是不可用的。
比如如下,右侧的对象是到GCRoot时不可达的,可以判定为可回收对象。
在java中,可以作为GCRoot的对象包括以下几种:
* 虚拟机栈中引用的对象。
* 方法区中静态属性引用的对象。
* 方法区中常量引用的对象。
* 本地方法中JNI引用的对象。
基于以上,我们可以知道,当当前对象到GCRoot中不可达时候,即会满足被垃圾回收的可能。
那么是不是这些对象就非死不可,也不一定,此时只能宣判它们存在于一种“缓刑”的阶段,要真正的宣告一个对象死亡。至少要经历两次标记:
第一次:对象可达性分析之后,发现没有与GCRoots相连接,此时会被第一次标记并筛选。
第二次:对象没有覆盖finalize()方法,或者finalize()方法已经被虚拟机调用过,此时会被认定为没必要执行。
如何回收
上述的两点讲解之后,我们大概明白了,哪些对象会被回收,以及回收的依据是什么,但回收的这个工作实现起来并不简单。
首先它需要扫描所有的对象,鉴别谁能够被回收,其次在扫描期间需要 ”stop the world“ 对象能被冻结,不然你刚扫描,他的引用信息有变化,你就等于白做了。
分代回收
我们从一个object1来说明其在分代垃圾回收算法中的回收轨迹。
1、object1新建,出生于新生代的Eden区域。
2、minor GC,object1 还存活,移动到Fromsuvivor空间,此时还在新生代。
3、minor GC,object1 仍然存活,此时会通过复制算法,将object1移动到ToSuv区域,此时object1的年龄age+1。
4、minor GC,object1 仍然存活,此时survivor中和object1同龄的对象并没有达到survivor的一半,所以此时通过复制算法,将fromSuv和Tosuv 区域进行互换,存活的对象被移动到了Tosuv。
5、minor GC,object1 仍然存活,此时survivor中和object1同龄的对象已经达到survivor的一半以上(toSuv的区域已经满了),object1被移动到了老年代区域。
6、object1存活一段时间后,发现此时object1不可达GcRoots,而且此时老年代空间比率已经超过了阈值,触发了majorGC(也可以认为是fullGC,但具体需要垃圾收集器来联系),此时object1被回收了。fullGC会触发 stop the world。
在以上的新生代中,我们有提到对象的age,对象存活于survivor状态下,不会立即晋升为老生代对象,以避免给老生代造成过大的影响,它们必须要满足以下条件才可以晋升:
1、minor gc 之后,存活于survivor 区域的对象的age会+1,当超过(默认)15的时候,转移到老年代。
2、动态对象,如果survivor空间中相同年龄所有的对象大小的综合和大于survivor空间的一半,年级大于或等于该年级的对象就可以直接进入老年代。
以上采用分代垃圾收集的思想,对一个对象从存活到死亡所经历的历程。期间,在新生代的时刻,会用到复制算法,在老年代时,有可能会用到标记-清楚算法(mark-sweep)算法或者标记-整理算法,这些都是垃圾回收算法基于不同区域的实现,我们看下这几种回收算法的实现原理。
垃圾回收算法
标记清除法(Mark-Sweep)
标记清除法是垃圾回收算法的思想基础。标记清除算法将垃圾分为两个阶段:标记阶段和清除阶段。
标记阶段,通过根节点,标记所有从根节点开始的可达对象,未标记过的对象就是未被引用的垃圾对象。
清除阶段,清除所有未被标记的对象。
复制算法(Copying)
复制算法是,将原有的内存空间分为两块,每次只使用其中一块,在垃圾回收时,将正在适用的内存中存活对象复制到未使用的内存块,然后清除使用的内存块中所有的对象。
标记压缩算法(Mark-Compact)
标记压缩算法是一种老年代的回收算法。
标记阶段和标记清除算法一致,对可达对象做一次标记。
清理阶段,为了避免内存碎片产生,将所有的存活对象压缩到内存的一端。
垃圾收集器
垃圾收集器是内存回收的具体实现,不同的厂商提供的垃圾收集器有很大的差别,一般的垃圾收集器都会作用于不同的分代,需要搭配使用。以下是各种垃圾收集器的组合方式:
各种组合的优缺点:
推荐一个交流学习群:650385180里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多:
JVM 调优系列之图解垃圾回收的更多相关文章
- java虚拟机学习-JVM调优总结-分代垃圾回收详述(9)
为什么要分代 分代的垃圾回收策略,是基于这样一个事实:不同的对象的生命周期是不一样的.因此,不同生命周期的对象可以采取不同的收集方式,以便提高回收效率. 在Java程序运行的过程中,会产生大量的对象, ...
- jvm 调优(2)垃圾回收算法
可以从不同的的角度去划分垃圾回收算法: 按照基本回收策略分 引用计数(Reference Counting): 比较古老的回收算法.原理是此对象有一个引用,即增加一个计数,删除一个引用则减少一个计数. ...
- java虚拟机学习-JVM调优总结-新一代的垃圾回收算法(11)
垃圾回收的瓶颈 传统分代垃圾回收方式,已经在一定程度上把垃圾回收给应用带来的负担降到了最小,把应用的吞吐量推到了一个极限.但是他无法解决的一个问题,就是Full GC所带来的应用暂停.在一些对实时性要 ...
- JVM调优-Jva中基本垃圾回收算法
从不同的的角度去划分垃圾回收算法. 按照基本回收策略分 引用计数(Reference Counting) 比较古老的回收算法.原理是此对象有一个引用,即增加一个计数,删除一个引用则减少一个计数.垃圾回 ...
- [转] JVM 调优系列 & 高并发Java系列
1.JVM调优总结(1):一些概念:http://www.importnew.com/18694.html 2.JVM调优总结(2):基本垃圾回收算法:http://www.importnew.com ...
- JVM调优系列:(四)GC垃圾回收
跟踪收集算法: 复制(copying): 将堆内分成两个同样空间,从根(ThreadLocal的对象.静态对象)開始訪问每个关联的活跃对象,将空间A的活跃对象所有拷贝到空间B,然后一次性回收整个空间A ...
- Android性能调优篇之探索垃圾回收机制
开篇废话 如果我们想要进行内存优化的工作,还是需要了解一下,但这一块的知识属于纯理论的,有可能看起来会有点枯燥,我尽量把这一篇的内容按照一定的逻辑来走一遍.首先,我们为什么要学习垃圾回收的机制,我大概 ...
- JVM调优系列:(五)JVM常用调试参数和工具
转自:http://blog.csdn.net/opensure/article/details/46715769 JVM常用调试参数: –verbose:gc在虚拟机发生内存回收时在输出设备显示信息 ...
- 【JVM调优系列】----CPU过高的分析与解决方案
1.首先通过top命令查询当前进程所占cpu的一个比重
随机推荐
- Javascript原型继承容易忽略的错误
编写Javascript的开发者都知道,JS虽然没有类(ES6添加了class语法),但是可以模拟出OOP语言的类和面向对象的概念,比如我们都知道的一句话,Javascript中处处是对象,而面向对象 ...
- 通过 AJAX 加载的 JavaScript 脚本的调试
//# sourceURL= 注意#后面有一个空格.
- 基于Spring Boot,使用JPA动态调用Sql查询数据
在<基于Spring Boot,使用JPA操作Sql Server数据库完成CRUD>,<基于Spring Boot,使用JPA调用Sql Server数据库的存储过程并返回记录集合 ...
- python数据分析工具包(3)——matplotlib(一)
前两篇文章简单介绍了科学计算Numpy的一些常用方法,还有一些其他内容,会在后面的实例中学习.下面介绍另一个模块--Matplotlib. Matplotlib是一个Python 2D绘图库,试图让复 ...
- ubuntu终端常用命令及solarized配色(护眼)
ubuntu终端常用命令及solarized配色(护眼) ubuntu 终端 命令 1.常用命令 ctrl + l - 清屏 . cLear ctrl + c - 终止命令. ctrl + d ...
- PHP中单引号与双引号的区别
在PHP中,字符串的定义可以使用英文单引号' ',也可以使用英文双引号" ". 一般情况下两者是通用的.但双引号内部变量会解析,单引号则不解析. PHP允许我们在双引号串中直接包含 ...
- Angular Universal(统一平台)笔记
angular官网高级文档AngularUniversal部分的翻译总结,这东西在angular4开始正式被官方支持了,目前其实支持的服务器端还没有很多,但好歹包括了node和DotNetCore,算 ...
- uva10976
数学题. 1. 因为 1/k = 1/x +1/y 所以 1/k > 1/y 那么 y > k 2 . 因为 x >= y 所以 1/k - 1/y <= 1/y 那么 y & ...
- POJ - 2492 种类并查集
思路:保存每个点与其父节点的关系,注意合并和路径压缩即可. AC代码 #include <cstdio> #include <cmath> #include <cctyp ...
- SpringBoot+Mybatis+PageHelper简化分页实现
前言 经过一段时间的测试和修改PageHelper插件逐渐走到了让我觉得靠谱的时候,它功能的就是简化分页的实现,让分页不需要麻烦的多写很多重复的代码. 已经加入我的github模版中:https:// ...