最新学习JVM这篇文章就够了!十个JVM的核心知识点懂得都懂~,为什么spring能最好地改变Java

完结

Redis基于内存,常用作于缓存的一种技术,并且Redis存储的方式是以key-value的形式。Redis是如今互联网技术架构中,使用最广泛的缓存,在工作中常常会使用到。Redis也是中高级后端工程师技术面试中,面试官最喜欢问的问题之一,因此作为Java开发者,Redis是我们必须要掌握的。

Redis 是 NoSQL 数据库领域的佼佼者,如果你需要了解 Redis 是如何实现高并发、海量数据存储的,那么这份腾讯专家手敲《Redis源码日志笔记》将会是你的最佳选择。

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

「死磕JVM」大吉大利:空投十个JVM核心知识点,速度捡包

2.3、四大引用类型

2.3.1 强引用

强引用(StrongReference)是使用最普遍的引用。垃圾回收器绝对不会回收它,内存不足时宁愿抛出OOM导致程序异常,平常的new 对象就是。

2.3.2 软引用

垃圾回收器在内存充足时不会回收软引用(SoftReference)对象,不足时会回收它,特别适合用于创建缓存。

2.3.3 弱引用

弱引用(WeakReference)是在扫描到该对象时无论内存是否充足都会回收该对象。 ThreadLocal 的Key就是弱引用。

2.3.4 虚引用

如果一个对象只具有虚引用(PhantomReference)那么跟没有任何引用一样,任何适合都可以被回收。主要用跟踪对象跟垃圾回收器回收的活动。

3、垃圾回收算法

========

为了挥手回收垃圾操作系统一般会使用 标记清除 、 复制算法 、 标记整理 三种算法,这三种各有优劣,简单介绍下:

3.1、标记清除

「死磕JVM」大吉大利:空投十个JVM核心知识点,速度捡包

原理:

算法分为 标记 和 清除 两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。

缺点:

标记清除之后会产生大量不连续的内存碎片,导致触发GC。

3.2、标记复制

「死磕JVM」大吉大利:空投十个JVM核心知识点,速度捡包

原理:

将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。

缺点:

这种算法的代价是将内存缩小为了原来的一半,还要来回移动数据。

3.3、标记整理

「死磕JVM」大吉大利:空投十个JVM核心知识点,速度捡包

原理:

首先标记出所有需要回收的对象,在标记完成后,后续步骤是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。

缺点:

涉及到移动大量对象,效率不高。

总结:

指标

标记清理

标记整理

标记复制

速度

中等

最慢

空间开销

少(但会堆积碎片)

少(不堆积碎片)

通常需要活对象的2倍大小(不堆积碎片)

移动对象

3.4 、三色标记跟读写屏障

前面说的三种回收算法都说到了先 标记 ,问题是如何标记的呢? 说话说一半,小心没老伴 !

接下来的知识点个人感觉面试应该问不到那么深了,但是为了 必须Mark下 !CMS 、 G1标记时候一般用的是 三色标记法 ,根据可达性分析从GC Roots开始进行遍历访问,可达的则为存活对象,而最终不可达说明就是需要被GC对象。大致流程是把遍历对象图过程中遇到的对象,按 是否访问过 这个条件标记成以下三种颜色:

  • 白色:尚未访问过。

  • 黑色:本对象已访问过,而且本对象 引用到 的其他对象 也全部访问过了。

  • 灰色:本对象已访问过,但是本对象 引用到 的其他对象 尚未全部访问完 。全部访问后会转换为黑色。

「死磕JVM」大吉大利:空投十个JVM核心知识点,速度捡包

假设现在有白、灰、黑三个集合(表示当前对象的颜色),遍历访问过程:

  • 1、初始时所有对象都在白色集合中。

  • 2、将GC Roots 直接引用到的对象挪到灰色集合中。

  • 3、从灰色集合中获取对象:第一步将本对象 引用到的 其他对象 全部挪到灰色集合中,第二步将本对象 挪到黑色集合里面。

  • 4、重复步骤3,直至灰色集合为空时结束。

  • 5、结束后仍在白色集合的对象即为GC Roots 不可达 ,可以尝试进行回收。

当STW时对象间的引用是不会发生变化的,可以轻松完成标记。当支持并发标记时,对象间的引用可能发生变化,多标和漏标的情况就有可能发生。

3.4 .1、浮动垃圾

状况:GC线程遍历到E(E是灰色),一个业务线程执行了D.E = null,此时E应该被回收的。但是GC线程已经认为E是灰色了会继续遍历,导致E没有被回收。

「死磕JVM」大吉大利:空投十个JVM核心知识点,速度捡包

3.4 .2、漏标

「死磕JVM」大吉大利:空投十个JVM核心知识点,速度捡包

GC线程遍历到E(灰色了)。业务线程执行了E–>G断开,D–>G链接的操作。GC线程发现E无法到达G,因为是黑色不会再遍历标记了。最终导致漏标G。

漏标的必备两个条件: 灰到白断开 , 黑到白建立 。

Object G = E.G; // 第一步 :读

Object E.G = null; // 第二步:写

Object D.G = G; // 第三步:写

复制代码

漏标解决方法:

将对象G存储到特定集合中,等并发标记遍历完毕后再对集合中对象进行 重新标记。

3.4.2.1、CMS方案

这里比如开始B指向C,但是后来B不指向C,A指向D,最简单的方法是 将A变成灰色 ,等待下次进行再次遍历。

「死磕JVM」大吉大利:空投十个JVM核心知识点,速度捡包

CMS中可能引发 ABA 问题:

1、回收线程 m1 正在标记A,属性A.1标记完毕,正在标记属性A.2。

2、业务线程 m2 把属性1指向了C,由于CMS方案此时回收线程 m3 把A标记位灰色。

3、回收线程 m1 认为所有属性标记完毕,将A设置为黑色,结果C漏标。所以CMS阶段需要重新标记。

「死磕JVM」大吉大利:空投十个JVM核心知识点,速度捡包

3.4.2.2、读写屏障

漏标的实现是有三步的,JVM加入了读写屏障,其中读屏障则是拦截第一步,写屏障用于拦截第二和第三步。

写屏障 + SATB(原始快照) 来破坏 灰到白断开。

写屏障 + 增量更新 来破坏 黑到白建立。

读屏障 一种保守方式来破坏灰到白断开后白的存储,此时用读屏障OK的。

现代使用可达性分析的垃圾回收器几乎都借鉴了三色标记的算法思想,尽管实现的方式不尽相同。对于读写屏障,以Java HotSpot VM为例,其 并发标记时对漏标的处理方案如下:

CMS : 写屏障 + 增量更新

G1 : 写屏障 + SATB

ZGC : 读屏障

CMS中使用的增量更新,在重新标记阶段除了需要遍历 写屏障的记录,还 需要重新扫描遍历GC Roots(标记过的不用再标记),这是由于CMS对于astore_x等指令不添加写屏障的原因。

4、GC流程

======

核心思想就是 根据各个年代的特点不同选用不同到垃圾收集算法 。

  1. 年轻代 :使用 复制算法

  2. 老年代 : 使用 标记整理 或者 标记清除 算法。

为什么要有年轻代:

分代的好处就是 优化GC性能 ,如果没有分代每次扫描所有区域能累死GC。因为很多对象几乎就是 朝生夕死 的,如果分代的话,我们把新创建的对象放到某一地方,当GC的时候先把这块存 朝生夕死 (80%以上)对象的区域进行回收,这样就会腾出很大的空间出来。

4.1、 年轻代

HotSpot JVM把年轻代分为了三部分:1个 Eden 区和2个 Survivor 区(分别叫from 和 to )。默认比例为 8:1:1 。一般情况下,新创建的对象都会被分配到 Eden区(一些大对象特殊处理),这些对象经过第一次Minor GC后,如果仍然存活,将会被移到 Survivor 区。对象在Survivor区中每熬过一次 Minor GC 年龄就会增加1岁,当它的年龄增加到一定次数(默认 15 次)时,就会被移动到年老代中。年轻代的垃圾回收算法使用的是 复制算法 。

「死磕JVM」大吉大利:空投十个JVM核心知识点,速度捡包

年轻代GC过程:

GC开始前,年轻代对象只会存在于 Eden 区和名为 From 的 Survivor 区,名为To 的 Survivor 区永远是空的。如果新分配对象在 Eden 申请空间发现不足就会导致GC。

yang GC : Eden 区中所有存活的对象都会被复制到 To ,而在 From 区中,仍存活的对象会根据他们的年龄值来决定去向。年龄达到一定值(年龄阈值可以通过 -XX:MaxTenuringThreshold来设置)的对象会被移动到年老代中,没有达到阈值的对象会被复制到 To 区域。经过这次GC后, Eden 区和 From 区已经被清空。这个时候, From 和 To 会交换他们的角色,也就是新的 To 就是上次GC前的 From ,新的 From 就是上次GC前的 To 。不管怎样 都会保证名为To的Survivor区域是空的 。 Minor GC 会一直重复这样的过程,直到 To 区被填满, To 区被填满之后,会将所有对象移动到年老代中。这里注意如果yang GC 后空间还是不够用则会 空间担保 机制将数据送到Old区

卡表 Card Table:

  1. 为了支持高频率的新生代回收 ,虚拟机使用一种叫做 卡表 (Card Table)的数据结构,卡表作为一个比特位的集合, 每一个比特位可以用来表示年老代的某一区域中的所有对象是否持有新生代对象的引用 。

  2. 新生代GC时不用花大量的时间扫描所有年老代对象,来确定每一个对象的引用关系, 先扫描卡表 ,只有卡表的标记位为1时,才需要扫描给定区域的年老代对象。而卡表位为0的所在区域的年老代对象,一定不包含有对新生代的引用。

4.2、 老年代

老年代GC过程:

老年代中存放的对象是存活了很久的,年龄大于15的对象 或者 触发了老年代的 分配担保 机制存储的大对象。在老年代触发的gc叫 major gc 也叫 full gc 。 full gc会包含年轻代的gc 。 full gc 采用的是 标记-清除 或 标记整理 。在执行 full gc 的情况下,会阻塞程序的正常运行。老年代的gc比年轻代的gc效率上 慢10倍以上 。对效率有很大的影响。所以 一定要尽量避免老年代GC !

4.3、 元空间

永久代的回收会随着 full gc 进行移动,消耗性能。每种类型的垃圾回收都需要特殊处理 元数据。将元数据剥离出来,简化了垃圾收集,提高了效率。

-XX:MetaspaceSize 初始空间的大小。达到该值就会触发垃圾收集进行类型卸载,同时GC会对该值进行调整:

如果释放了大量的空间,就适当降低该值;

如果释放了很少的空间,那么在不超过MaxMetaspaceSize时,适当提高该值。

-XX:MaxMetaspaceSize:

最大空间,默认是没有限制的。

4.4 、垃圾回收流程总结

「死磕JVM」大吉大利:空投十个JVM核心知识点,速度捡包

大致的 GC回收流程 如 上图 ,还有一种设置就是 大对象直接进入老年代 :

  1. 如果在新生代分配失败且对象是一个不含任何对象引用的大数组,可被直接分配到老年代。通过在老年代的分配避免新生代的一次垃圾回收。

  2. 设置了-XX:PretenureSizeThreshold 值,任何比这个值大的对象都不会尝试在新生代分配,将在老年代分配内存。

内存回收跟分配策略

  1. 优先在Eden上分配对象,此区域垃圾回收频繁速度还快。

  2. 大对象直接进入老生代。

  3. 年长者(长期存活对象默认15次)跟 进入老生代。

  4. 在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象会群体进入老生代。

  5. 空间分配担保(担保minorGC),如果Minor GC后 Survivor区放不下新生代仍存活的对象,把Suvivor 无法容纳的对象直接进入老年代。

5、垃圾收集器

=======

5.1、 垃圾收集器

堆heap是垃圾回收机制的重点区域。我们知道垃圾回收机制有三种 minor gc 、major gc 和 full g c。针对于堆的就是前两种。年轻代的叫 minor gc ,老年代的叫 major gc 。

  1. JDK7、JDK8 默认垃圾收集器 Parallel Scavenge(新生代)+ Parallel Old(老年代)

  2. JDK9 默认垃圾收集器 G1 ,服务端开发常见组合就是 ParNew + CMS

「死磕JVM」大吉大利:空投十个JVM核心知识点,速度捡包

工程化使用的时候使用指定的垃圾收集器组合使用,讲解垃圾收集器前先普及几个重要知识点:

STW

java中 Stop-The-World 机制简称STW,是指执行垃圾收集算法时Java应用程序的其他所有线程都被挂起 (除了垃圾收集帮助器之外)。是Java中一种全局暂停现象,全局停顿,所有Java代码停止,native代码虽然可以执行但不能与JVM交互,如果发生了STW 现象多半是由于gc引起 。

吞吐量

吞吐量 = 运行用户代码时间 / ( 运行用户代码时间 + 垃圾收集时间 )。例如:虚拟机共运行100分钟,垃圾收集器花掉1分钟,那么吞吐量就是99%

垃圾收集时间

垃圾回收频率 * 单次垃圾回收时间

并行收集

指多条垃圾收集线程并行工作,但此时用户线程仍 处于等待状态 。

并发收集

指 用户线程与垃圾收集线程同时工作 (不一定是并行的可能会交替执行)。用户程序在继续运行,而垃圾收集程序运行在另一个CPU上。

5.2、 新生代

新生代有 Serial 、 ParNew 、 Parallel Scavenge 三种垃圾收集器。

名称

串行/并行/并发

回收算法

使用场景

可以跟CMS配合

Serial

串行

复制

单CPU,Client模式下虚拟机

ParNew

并行(Serial的并行版)

复制

多CPU,常在Server模式

Parallel Scavenge

并行

复制

多CPU且关注吞吐量

5.3、 老年代

老年代有 Serial Old 、 Parallel Old 、 CMS 三种垃圾收集器。

名称

串行/并行/并发

回收算法

使用场景

组合年轻代

Serial Old

串行

标记整理

单CPU

Serial 、ParNew、Parallel Scavenge

Parallel Old

并行

标记整理

多CPU

Parallel Scavenge

CMS

并发

标记清除

多CPU且关注吞吐量,常用Server端

Serial 、ParNew

5.3.1、CMS

CMS (Concurrent Mark Sweep)比较重要这里 重点说一下 。

CMS的初衷和目的:

为了消除Throught收集器和Serial收集器在Full GC周期中的长时间停顿。是一种以获取最短回收停顿时间为目标 的收集器,具有自适应调整策略,适合互联网站 跟B/S 服务应用。

CMS的适用场景:

如果你的应用需要 更快的响应 ,不希望有长时间的停顿,同时你的 CPU资源也比较丰富 ,就适合使用CMS收集器。比如常见的Server端任务。

优点:

并发收集、低停顿。

缺点:

  1. CMS收集器对CPU资源非常敏感 :在并发阶段,虽然不会导致用户线程停顿,但是会占用CPU资源而导致引用程序变慢,总吞吐量下降。

  2. 无法处理浮动垃圾 :由于CMS并发清理阶段用户线程还在运行,伴随程序的运行自然会有新的垃圾不断产生,这一部分垃圾出现在标记过程之后,CMS无法在本次收集中处理它们,只好留待下一次GC时将其清理掉。这一部分垃圾称为 浮动垃圾 。 如果内存放不下浮动垃圾这时 JVM 启动 Serial Old 替代 CMS 。

  3. 空间碎片 :CMS是基于 标记-清除 算法实现的收集器,使用 标记-清除 算法收集后,会产生 大量碎片 。

CMS回收流程:

  1. 初始标记 : 引发STW , 仅仅只是标记出GC ROOTS能直接关联到的对象,速度很快。

  2. 并发标记 : 不引发STW ,正常运行 所有Old 对象是否可链到GC Roots

  3. 重新标记 : 引发STW ,为了修正并发标记期间,因用户程序继续运作而导致标记产生改变的标记。这个阶段的停顿时间会被初始标记阶段稍长,但比并发标记阶段要短。

  4. 并发清除 : 不引发STW ,正常运行,标记清除算法来清理删除掉标记阶段判断的已经死亡的对象。

总结:

  1. 并发标记 和 并发清除 的耗时最长但是不需要停止用户线程。 初始标记 和重新标记 的耗时较短,但是需要停止用户线程,所以整个GC过程造成的停顿时间较短,大部分时候是可以和用户线程一起工作的。

之前的GC收集器对Heap的划分:

「死磕JVM」大吉大利:空投十个JVM核心知识点,速度捡包

以前垃圾回收器是 新生代 + 老年代 ,用了CMS效果也不是很好,为了减少STW对系统的影响引入了G1(Garbage-First Garbage Collector), G1 是一款面向服务端应用的垃圾收集器,具有如下特点:

1、 并行与并发 :G1能充分利用多CPU、多核环境下的硬件优势,可以通过并发的方式让Java程序继续执行。

2、 分代收集 :分代概念在G1中依然得以保留,它能够采用不同的方式去处理新创建的对象和已经存活了一段时间、熬过多次GC的旧对象来获得更好的收集效果。

3、 空间整合 :G1从整体上看是基于 标记-整理 算法实现的,从局部(两个Region之间)上看是基于 复制算法 实现的,G1运行期间不会产生内存空间碎片。

4、 可预测停顿 :G1比CMS牛在能建立可预测的停顿时间模型,能让使用者明确指定在一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒。

G1作为JDK9之后的服务端默认收集器,不再区分年轻代和老年代进行垃圾回收,G1默认把堆内存分为N个分区,每个1~32M(总是2的幂次方)。并且提供了四种不同Region标签 Eden 、 Survivor 、 Old 、 Humongous 。H区可以认为是Old区中一种特别专门用来存储大数据的,关于H区数据存储类型一般符合下面条件:

当 0.5 Region <= 当对象大小 <= 1 Region 时候将数据存储到 H区

当对象大小 > 1 Region 存储到连续的H区。

「死磕JVM」大吉大利:空投十个JVM核心知识点,速度捡包

同时G1中引入了 RememberSets 、 CollectionSets 帮助更好的执行GC 。

1、 RememberSets : RSet 记录了其他Region中的对象引用本Region中对象的关系,属于points-into结构(谁引用了我的对象)

2、 CollectionSets : Csets 是一次GC中需要被清理的regions集合,注意G1每次GC不是全部region都参与的,可能只清理少数几个,这几个就被叫做Csets。在GC的时候,对于old -> young 和old -> old的跨代对象引用,只要扫描对应的 CSet中的 RSet 即可。

G1进行GC的时候一般分为 Yang GC 跟 Mixed GC 。

Young GC : CSet 就是所有年轻代里面的Region

Mixed GC : CSet 是所有年轻代里的Region加上在全局并发标记阶段标记出来的收益高的Region

5.4.1、Yang GC

「死磕JVM」大吉大利:空投十个JVM核心知识点,速度捡包

标准的年轻代GC算法,整体思路跟CMS中类似。

5.4.2、Mixed GC

G1中是  有Old GC的,有一个把老年代跟新生代同时GC的 Mixed GC,它的 回收流程 :

最后

image.png

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

G1进行GC的时候一般分为 Yang GC 跟 Mixed GC 。

Young GC : CSet 就是所有年轻代里面的Region

Mixed GC : CSet 是所有年轻代里的Region加上在全局并发标记阶段标记出来的收益高的Region

5.4.1、Yang GC

「死磕JVM」大吉大利:空投十个JVM核心知识点,速度捡包

标准的年轻代GC算法,整体思路跟CMS中类似。

5.4.2、Mixed GC

G1中是  有Old GC的,有一个把老年代跟新生代同时GC的 Mixed GC,它的 回收流程 :

最后

[外链图片转存中…(img-7xAn7Nwo-1715669090794)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

  • 30
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值