JVM GC知识(一)- GC算法

632 阅读5分钟

前言

每个使用 Java 的开发者都知道 Java 字节码是在 JRE 中运行。 JVM 则是 JRE 中的核心组成部分,承担分析和执行 Java 字节码的工作,而 Java 程序员通常并不需要深入了解 JVM 运行情况就可以开发出大型应用和类库。尽管如此,如果你对 JVM 有足够了解,就会对 Java 有更好的掌握,并且能解决一些看起来简单但又尚未解决的问题。

正文

JVM的运行时数据区

首先,我们来看一下 JVM 不得不知道的体系结构,在编译完之后的 class 文件装载到类装载器的运行时数据区,运行时数据区分为五大块,分别是方法区,堆, Java 栈,本地方法栈,程序计数器。Java栈,本地方法栈,程序计数器存储本地方法接口,方法区和堆则存储执行引擎,也是 GC 的作用区域。

运行时数据区

GC分代

JDK1.8之前,内存管理机制采用分代的策略:分为新生代,老年代,永久代(JDK1.7及以前)。

在这里插入图片描述

新生代

大致分为 Eden 区和 Survivor 区, Survivor 区又分为大小相同的两部分: s0 ( FromSpace ) 和 s1 ( ToSpace )。新建的对象都是从新生代分配内存, Eden 区不足的时候,会把存活的对象转移到 Survivor 区。当新生代进行垃圾回收时会触发 Minor GC 。一般在Eden区分配对象,使用 TLAB( Thread Local Allocation Buffer ) 进行优化,在保存80%~90%生命周期较短的对象,GC频率高,采用效率较高的复制算法。

为何eden:s0:s1 = 8:1:1?

新生代的三个分区默认比例为 8:1:1 ,JVM中规定 98% 的对象要在新生代被回收,而 s0 及 s1 只是作为暂存区,每次GC的触发之后必须保证其中一个区是空的,随后两个区互换位置,及 s0 -> s1 (from -> to),s1 -> s0 (to -> from),保证绝大部分对象在此被回收。

老年代

旧生代用于存放新生代多次回收依然存活的对象,如缓存对象。当旧生代满了的时候就需要对旧生代进行回收,旧生代的垃圾回收称作 Major GC。新建的对象也有可能直接在旧生代中分配,取决于具体GC的实现。GC频率相对较低,标记,清理,压缩算法的各种结合和优化。

新生代晋升老年代的条件

  1. Eden 区满时,进行 Minor GC,当 Eden 和一个 Survivor 区中依然存活的对象无法放入到 Survivor 中,则通过分配担保机制提前转移到老年代中。
  2. 若对象体积太大, 新生代无法容纳这个对象,-XX:PretenureSizeThreshold 即对象的大小大于此值, 就会绕过新生代, 直接在老年代分配, 此参数只对 Serial 及 ParNew 两款收集器有效。
  3. 长期存活的对象将进入老年代。 虚拟机对每个对象定义了一个对象年龄(Age)计数器。当年龄增加到一定的临界值时,就会晋升到老年代中,该临界值由参数:-XX:MaxTenuringThreshold 来设置。 如果对象在 Eden 出生并在第一次发生 MinorGC 时仍然存活,并且能够被 Survivor 中所容纳的话,则该对象会被移动到 Survivor 中,并且设 Age = 1 ;以后每经历一次 Minor GC ,该对象还存活的话 Age = Age+1。
  4. 动态对象年龄判定。 虚拟机并不总是要求对象的年龄必须达到 MaxTenuringThreshold 才能晋升到老年代,如果在 Survivor 区中相同年龄(设年龄为 age )的对象的所有大小之和超过 Survivor 空间的一半,年龄大于或等于该年龄(age)的对象就可以直接进入老年代,无需等到 MaxTenuringThreshold 中要求的年龄。

永久代

对象经历了多次 Major GC 仍然没有被回收即进入永久代,仅 JDK1.7 及之前的版本拥有。

判断可回收的垃圾(GC对象)

我们首先来看一下哪些是GC的目标,GC中使用的回收检测算法有两种,目前虚拟机基本上都是用可达性算法:

引用计数法

每个对象有一个引用计数器,当对象被引用一次则计数器加1,当对象引用失效一次则计数器减1,对于计数器为0的对象意味着是垃圾对象,可以被GC回收。

在这里插入图片描述

可达性算法

从GC Roots作为起点开始搜索,那么整个连通图中的对象便都是活对象,对于GC Roots无法到达的对象便成了垃圾回收的对象,随时可被GC回收。

在这里插入图片描述

常见垃圾回收算法-方法论

标记-清除算法

原理
标记

从根集合开始扫描,对存活的对象进行标记。

在这里插入图片描述

清除

扫描整个内存空间,回收未被标记的对象,使用free-list记录可回收区域。

在这里插入图片描述

复制回收算法

原理
  1. 从根集合开始,通过Tracing从from中找到存活对象,拷贝到to中;
  2. from、to交换身份,下次内存分配从to开始。
    在这里插入图片描述

标记-整理算法

原理
标记

与标记-清除一样

在这里插入图片描述

压缩

再次扫描,并往一段滑动存活对象

在这里插入图片描述

标记-清除-压缩

原理
  1. Mark-Sweep和Mark-Compact的结合
  2. 和Mark-Sweep一致,当进行多次GC后才Compact
    在这里插入图片描述

By the way

有问题?可以给我留言或私聊 有收获?那就顺手点个赞呗~

当然,也可以到我的公众号下「6曦轩」,

回复“学习”,即可收到一份 【Java工程师进阶架构师的视频教程】~

回复“面试”,可以收到一套 【本人呕心沥血整理的Java面试题目】(部分目录如下)

面试题目录

面试题目录

Checkit 公众号
由于我咧,科班出身的程序员,php,Android以及硬件方面都做过,不过最后还是选择专注于做 Java,所以有啥问题可以到公众号提问讨论(技术情感倾诉都可以哈哈哈),看到的话会尽快回复,希望可以跟大家共同学习进步,关于服务端架构,Java 核心知识解析,职业生涯,面试总结等文章会不定期坚持推送输出,欢迎大家关注~~~