JVM细节拆截导图(面试必备)
2026-01-24 03:07:29 0 举报
JVM,面试,类加载,java
作者其他创作
大纲/内容
虚拟机完成类装载过程后,将class文件中的常量池载入
最终标记
老年代采用整理算法暂停所有用户线程
........
可能会发生,需要同时满足3种情况:1、该类所有的实例都已经被回收,堆中不存在该类的任何实例2、加载该类的ClassLoader已经被回收3、该类对应的java.lang.Class对象没有被引用,无法通过反射访问该类的方法
GC 线程
安全点
对象头设置
用户线程
JVM启动时创建并与JVM进程绑定,是堆的逻辑部分,内存不足时会抛出OOM异常
OOP
MarkWord
垃圾判断算法
断开
验证
偏向状态01
重新标记
字面量:文本字符串final常量值基本数据类型值其他
Y
清楚标记阶段判断已经死亡的对象
To(S2)
后备预案
本地方法库Native Libraries
Serial(串行)复制
对象在内存中存储布局分为3块区域:对象头、实例数据和对齐填充数据
G1
语义分析器
实例池
Old
本地方法栈1. 这个区域与虚拟机栈类似,但它是为JVM使用到的Native方法服务的。在执行Native方法时,这些方法的栈帧会被分配在本地方法栈上。2. 本地方法栈也会在栈深度溢出或者栈扩展失败时分别抛出StackOverflowError和OOM异常。
新生代(1/3)MinorGC
整体对象创建流程:
G1 主要是面向服务端的垃圾应用器G1 认为只要大小超过了一个Region 容量一半的对象即可判定为大对象,大对象存储在 Humongous 中,对于超过了整个 Region 的超级大对象,会放在 N 个连续 Humongous 中G1 可以控制收集的停顿时间主要是维护了一个优先级列表,每次会优先处理价值(回收所获得的空间和回收所获得时间的经验值)最大的 RegionG1 为每个 Region 设计了两个名为 TAMS 的指针,把 Region 中的一部分空间划分出来用户并发回收过程中的新对象分配,并发回收时新分配的对象地址都必须要在这两个指针之上记忆表的结构:Key 是别的 Region 的起始地址,Vlaue 是一个集合,里面存储的元素是卡表的索引G1 从整体看是标记整理算法,从局部(两个 Region 之间)上看是标记复制算法
装载步骤:1. 通过全限定名获取class字节码文件二进制字节流2. 将静态结构载入方法区转换为运行时数据结构(类信息、静态变量、常量,即时编译的热点代码不在这个阶段进入方法区)3.在堆中生成一个代表该类的java.lang.Class对象(作为存储在方法区中对应数据的访问入口)注:在装载阶段可以对类加载器进行操作,还可以对字节码进行增强操作
运行时常量池
使用内存映射解决修改指针后操作系统。处理器不支持的情况Linux/x86-64 平台上的 ZGC 使用多重映射将对个不同的虚拟内存地址映射到同一个物理内存地址上,这是一种多对一的映射。把染色指针的标志位看作是地址的分段符,那只要将这些不同的地址段都映射到同一个物理内存空间,经过多重映射转换后,就可以使用染色指针进行正常寻址
对象类型数据
Serial Old 是 Serial 收集器的老年代版本,同样是一个单线程收集器,主要意义也是在客户端模式下的 HotSpot 虚拟机使用在服务端模式下主要用途:JDK 5以及之前的版本中与 Parallel Scavenge 收集器搭配使用;作为 CMS 收集器发生失败时的后备预案,在并发收集发生 \"Concurrent Mode Failure\" 时使用;
垃圾收集(GC)GC是自动内存管理的一个重要部分,GC 的主要任务是识别并丢弃那些不再被程序使用的对象以释放内存空间,提高JVM的内存使用率。垃圾收集类型指垃圾收集过程中的具体行为或阶段。Minor GC: 定义:Minor GC 指的是在年轻代(Young Generation)中发生的垃圾收集过程。 触发时机:当年轻代的 Eden 区或者 Survivor 区满了,JVM 就会触发 Minor GC。 操作:在 Minor GC 中,存活的对象会从 Eden 区复制到一个 Survivor 区,或者从一个 Survivor 区复制到另一个 Survivor 区(S0 和 S1 通常会互换角色)。不再存活的对象(没有任何引用指向的对象)将被回收。 停顿:Minor GC 通常会引起短暂的停顿,即 Stop-The-World(STW)事件,此时所有用户线程都会暂停,直到垃圾收集完成。Major GC: 定义:Major GC 指的是在老年代(Old Generation)中发生的垃圾收集过程。 触发时机:当老年代的空间不足以容纳新晋升的对象时,JVM 会触发 Major GC。 操作:Major GC 通常会回收老年代中不再存活的对象,但它的执行时间通常比 Minor GC 长,因为它涉及到更多的数据和更复杂的数据结构。 停顿:Major GC 也会引起 STW 事件,但由于涉及的数据量通常比 Minor GC 多,因此停顿时间也会更长。Full GC: 定义:Full GC 是对整个 Java 堆内存(包括年轻代、老年代和永久代/元空间)的垃圾收集。 触发时机:Full GC 可以由多种原因触发,例如系统显式调用 System.gc(),老年代空间不足,永久代/元空间内存不足,JVM 内部调优失败等。 1. 调用System.gc时,系统建议执行Full GC,但是不必然执行 2. 老年代空间不足、方法区空间不足 3. 通过Minor GC后进入老年代的平均大小大于老年代的可用内 4. 由Eden区、survivor space1(From Space)区向survivor space2(To Space)区复制时,对象大小大于To Space可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小 5. 当永久代满时也会引发Full GC,会导致Class、Method元信息的卸载 操作:在 Full GC 过程中,JVM 会清理整个堆内存和方法区(在 JDK 8 之前称为永久代,之后称为元空间),包括年轻代和老年代中的所有对象。 停顿:Full GC 通常会导致最长时间的 STW 停顿,因为它是最彻底的垃圾收集过程。垃圾收集器负责自动管理内存的系统,不同的垃圾收集器采用不同的算法和策略来完成这项工作。垃圾收集器的设计和选择会直接影响垃圾收集类型的行为、频率、持续时间以及对应用程序性能的影响Serial GC 使用单线程执行 Minor GC 和 Full GC。Parallel GC 使用多线程并行执行 Minor GC,但 Full GC 通常是单线程的。CMS GC 旨在减少 Full GC 的停顿时间,通过并发标记和并发清除来实现。G1 GC 将堆分割成多个区域(Region),并对这些区域进行增量式的清理,旨在平衡吞吐量和停顿时间,同时减少 Full GC 的发生。ZGC 和 Shenandoah GC 都是低停顿时间垃圾收集器,它们通过并发的方式执行几乎所有的垃圾收集工作,从而减少 Full GC 的发生和停顿时间。垃圾收集器可以根据它们的工作模式被分类为:串行(Serial)垃圾收集器:在执行垃圾收集时使用单个线程,在执行垃圾收集过程中会暂停应用程序的所有线程(Stop-The-World,STW)。并行(Parallel)垃圾收集器:在执行垃圾收集工作时使用多个线程,也会引发应用程序的STW暂停,但由于使用了多线程,所以通常比串行收集器快并发(Concurrent)垃圾收集器:在收集垃圾时尽量与应用程序线程同时运行,从而减少STW的发生和持续时间,适合对停顿时间敏感的应用程序选择哪种类型的垃圾收集器取决于应用程序的需求和工作负载特性,对于需要最大化吞吐量的后台处理应用程序,可能会选择并行垃圾收集器。而对于需要快速响应用户交互的前端应用程序,可能会选择并发垃圾收集器以减少停顿时间并行:多个垃圾收集线程进行垃圾收集,业务代码线程停止,更关注吞吐量(Parallel GC)。并发:垃圾收集线程与用户线程同时执行,更关注停顿时间(CMS、G1、ZGC)。实际配置需结合具体场景评估,常见做法包括:吞吐量优先:如批处理系统,优先选择Parallel GC。延迟敏感:如高并发Web服务,可能采用ParNew + CMS或G1 GC。调优与验证:通过GC日志分析和压力测试观察停顿时间和CPU消耗,例如在A/B测试中对比不同配置GC的次数很频繁怎么办?几个维度: 1、Minor gc:可能young区空间不够,或eden、s0、s1比例分配不合理。 2、Major gc:old区空间不够,如young区不断有存活对象晋升到old区。频繁有大对象被分配到old区。 3、metaspace空间不够。
新生代采用复制算法暂停所有用户线程
Age
字节码
小型 Region
中型 Region
大型 Region
数据长度(数据对象才有)
内存分配END
编译结果(对应平台可执行文件)
class字节码文件
对象访问方式
Eden区(8/10)
reference
染色指针
空间不足
float
JDK 9 取消组合支持
虚拟机栈
实例对象
初始化
局部变量表
字符串常量池
指向新的引用
更新 Region 的统计信息,对 Region 的回收价值和成本排序,制定回收计划。需要 STW
Bar
解释执行器Java程序开始运行时,解释器负责读取字节码指令,逐条读取逐条执行。这种方式效率不高,因为每次执行时都需要解释指令。
并发标记
垃圾回收
Token流
CodeCacheJIT编译代码产物
重置线程
方法出口
Finalizable
Remapped
Markedl
Marked0
对象实例数据
Full GC
对象头
筛选回收
Serial
老年代采用标记-整理算法,暂停所有用户线程
对象的HashCode
Data1
int
Brooks Pointer
ZGC 收集器是一款基于 Region 内存布局的,(暂时) 不设分代,使用了读屏障、染色指针和内存多重映射等技术来实现可并发标记-整理算法的,以低延迟为首要目标的一款垃圾收集器。
1、常量池符号引用替换为直接引用同一个符号可能被多次解析,除了invokedynamic指令,jvm会对第一次解析结果进行缓存2、“解析”有可能发生在“初始化”后(运行时绑定/晚期绑定:在运行时根据对象具体的类型进行绑定)
对象大小计算1. 在32位系统下,存放Class指针的空间大小是4字节,MarkWord是4字节,对象头为8字节。2. 在64位系统下,存放Class指针的空间大小是8字节,MarkWord是8字节,对象头为16字节。3. 64位开启指针压缩的情况下,存放Class指针的空间大小是4字节,MarkWord是8字节,对象头为12字节。数组长度4字节+数组对象头8字节(对象引用4字节(未开启指针压缩的64位为8字节)+数组markword为4字节(64位未开启指针压缩的为8字节))+对齐4=16字节。4. 静态属性不算在对象大小内。
修正并发期间,因用户线程继续运行导致标记产生变动的那些对象的标记记录(增量更新),需要 STW
指向对象类型数据的指针
解析
对齐填充(选填)
MinorGC 复制
操作数栈主要负责在方法执行过程中临时存储计算的中间结果,同时也用于存储指令的输入参数和输出结果。
类型指针
初始标记仅仅只是标记一下GC Roots 能关联的对象,速度很快,需要 STW
垃圾回收算法一、标记清除算法(老年代):标记可达对象,清除未被标记的对象,会产生内存碎片,导致内存空间的不连续,后续可能更频繁的GC。 0. 首先标记出所有需要回收的对象,在标记完成后,统一回收掉所有被标记的对象,也可以反过来,标记存活的对象,统一回收所有未被标记的对象 1. 执行效率不稳定:如果Java堆中包含大量对象,而且其中大部分是需要被回收的,这时必须进行大量标记和清除的动作,导致标记和清除两个过程的执行效率都随对象数量增长而降低 2. 内存空间的碎片化问题:标记与清除之后会产生大量不连续的内存碎片,碎片太多可能会导致当以后在程序运行过程中需要分配较大对象时无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作二、标记复制算法(年轻代):需要开辟一块备用空间来处理GC操作 0. 为了解决标记-清除算法面对大量可回收对象时执行效率低的问题(存活对象少的情况,如年轻代) 1. 每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。 2. 优点:算法需要复制的就是占少数的存活对象,而且每次都是针对整个半区进行内存回收,分配内存时也就不用考虑有空间碎片的复杂情况只要移动堆顶指针,按顺序分配即可。这样实现简单,运行高效 3. 缺点:将可用内存缩小为了原来的一半,空间浪费未免太多了一点 4. 内存的分配担保:如果另外一块Survivor空间没有足够空间存放上一次新生代收集下来的存活对象,这些对象便将通过分配担保机制直接进入老年代三、标记整理算法(老年代):标记可达对象进行整理,整理碎片有性能开销 0. 解决标记-复制算法在对象存活率较高时就要进行较多的复制操作,效率将会降低以及空间浪费的问题 1. 标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向内存空间一端移动,然后直接清理掉边界以外的内存 2. Stop The World:尤其在老年代这种每次回收都有大量对象存活区域,移动存活对象并更新所有引用这些对象的地方将会是一种极为负重的操作,而且这种对象移动操作必须全程暂停用户应用程序才能进行 3. 是否移动对象都存在弊端,移动则内存回收时会更复杂,不移动则内存分配时会更复杂 4. CMS收集器面临空间碎片过多时:平时多数时间都采用标记-清除算法暂时容忍内存碎片的存在,直到内存空间的碎片化程度已经大到影响对象分配时,再采用标记-整理算法收集一次以获得规整的内存空间内存效率:复制算法>标记清除算法>标记整理算法(非绝对)内存整齐度:复制算法=标记整理算法>标记清除算法内存利用率:标记整理算法=标记清除算法>复制算法哪种垃圾回收算法最好?各有长短,正所谓分代垃圾回收算法,不同的区域使用不同的算法回收垃圾对象。
ParNew(并行)复制
来回替换同一时刻永远有一个空的
DataN
Mark Word
ParNew
销毁
词法分析器
JDK 5以及之前搭配使用
new A()
是
连接矩阵可以理解为一张二维表格,如果 Region N 有指向对象指向 Region M,就在表格的 N 行 M 列中打上一个标记
Serial Old(MSC)
实例数据
使用直接指针来访问最大的好处就是速度更快,它节省了一次指针定位的时间开销,由于对象访问在Java中非常频繁,因此这类开销积少成多也是一项极为可观的执行成本,虚拟机HotSpot使用此方式
否
初始标记
Application(app)类加载器(派生于ClassLoader类)负责加载环境变量classpath或系统属性java.class.path指定路径下的类库
栈帧
.........
Epoch
Humongous
伊甸园
N
Parallel Old 是 Parallel Scaenge 收集器的老年代版本,支持多线程并发收集,基于标记整理算法适合在注重吞吐量或者处理器资源较为稀缺的场合使用
数组
将分配到的内存空间都初始化为零值(不包括对象头)
动态链接
Serial Old(串行)整理
单线程串行 GC,在进行垃圾收集时,必须暂停是所有的工作线程。算法:标记-复制算法客户端模式下默认新生代收集器对于其他收集器,Serial 收集器简单高效,在内存资源首先环境中,它是所有收集器中额外内存(保证垃圾回收器能顺利高效进行而存储的额外信息)消耗最小的
类的加载过程
句柄访问对象
JVM的参数类型1. 标准参数 * -help、-server、-client、-version、-showversion、-cp 、-classpath2. X参数(非标准化参数(在各个JDK版本中可能会变,但是变动比较小) * -Xint : 解释执行 * -Xcomp : 编译执行 第一次使用就编译成本地代码 * -Xmixed :混合模式,JVM自己来决定是否编译成本地代码3. XX参数(非标转化参数,相对不稳定,主要用于JVM调优和Debugspan style=\"font-size:inherit;\
大对象 ?
JVM启动时创建并与JVM进程绑定,内存不足时会抛出OOM异常
找出要回收的对象,还要重新整理 SATB(原始快照) 记录下的在并发时有引用变动的对象
JVM运行时数据区
Extension(EXT)类加载器(派生于ClassLoader类)负责加载扩展jar包
Class Pointer类型指针(指向它的类型元数据的指针)
标记复制算法(年轻代)
线程私有区域
Parallel Old(并行)整理
From(S1)
1、执行类构造器<clinit>()2、初始化静态变量值(如果有,初始化父类静态变量值)3、执行静态代码块(如果有,执行父类静态代码块)4、执行构造方法(如果有,执行父类构造方法)初始化触发时机: 1、主动引用(一定会发生类的初始化) 1)创建类的实例,new的方式 2)访问某个类或接口的静态变量,或对该静态变量赋值 3)调用类的静态方法 4)反射 5)初始化某个类的子类,则其父类也会被初始化 6)Java虚拟机启动时被标记为启动类的类(“JvmCaseApplication”), 直接使用“java.exe”命令来运行某个类主类 2、被动引用(不会发生类的初始化) 1)当访问某个类的静态变量时,只有声明这个静态变量的类会被初始化 2)引用常量不会此类的初始化(常量在连接阶段就已经存入类的常量池中) 3)通过数组定义类的引用“A[] a = new a[10]”,不会触发此类(A)的初始化
8种基本数据类型
方法区 | 永久代1.7 | 元空间1.8( Meta Space)
span style=\
栈帧...
TLAB(线程私有)
进行加载
CMS
Java源码
G1(并发)整理+复制
检查类是否已被加载、解析、初始化
JAVA堆
并发清理
CMS(并发)清除
指针碰撞
栈
字节码文件加载途径:1、从本地系统直接加载2、通过网络下载.class文件3、从归档文件中加载.class文件(jar、war、zip)4、从专有数据库中提取.class文件5、将Java源文件动态编译为.class文件(运行时计算而成:动态代理)6、从加密文件中获取
内存分配方式
字节码生成器
本地线程分配缓冲(TLAB)
TreadID
初始标记:与 G1 一样,首先标记与 GC Roots 之间关联的对象,这个阶段仍是 STW并发标记:与 G1 一样,遍历对象图,标记出全部可达的对象最终标记:与 G1 一样,处理剩余的 SATB 扫描,并在这个阶段统计出回收价值最高的 Region,将这些 Region 构成一组回收集,需要 STW并发清理:这个阶段用于清除那些整个内存连一个存活对象都没有找到的 Region并发回收:把回收集存活的对象先复制一份到其他未被使用的 Region 中,Shenandoash 会通过读屏障和被称为 Brooks Pointers 来解决并发场景复制对象的问题。初始引用更新:引用更新是并发回收阶段复制对象结束后,还需要把堆中所有指向接对象的引用指向修正到复制后的新地址。初始引用更新是建立一个线程集合点,确保所有并发回收阶段中进行的收集器线程都已完全分配给她们的对象移动任务,会产生一个 STW并发引用更新:真正更新引用更新操作,按照内存物理地址的顺序,线性的搜索出引用类型,把旧值改为新值。最终引用更新:解决了堆中的引用更新后,还需要修正存在于 GC Roots 中的引用,需要 STW并发清理:经过并发回收之后和引用更新后,整个回收集中所有的 Region 已为存活对象,再次调用并发清理过程来回收这些 Region 的内存空间
实例通过Class模板创建而来
对象
Parallel Scavenge(并行)复制
Header
老年代
堆(Heap)FullGC
对齐填充-8字节倍数
Foo
Age超限?
Region Number
1
2
3
4
5
6
返回地址类型
为对象分配内存
程序计数器
方法区
包括:哈希码、GC分代年龄、锁状态标志、线程持有的锁、偏向锁、偏向ID、偏向时间戳
引用计数
本地接口Native Interface
直接指针访问对象
准备
age=15
垃圾收集器(连线代表可搭配使用)
方法出口(也称为方法返回地址)在方法执行完毕后起到了控制程序流程的作用。1、返回到方法调用者2、恢复调用者的栈帧状态3、传递方法返回值4、异常处理
TLAB ?
Region 1
Region 2
Region 3
Region 4
Region 5
Survivor(s0 From)1/10
JIT Compiler即时编译器
句柄池
Parallel Scavenge
染色指针优势:染色指针可以使得一旦某个 Region 的存活对象被移走之后,这个 Region 立即就能够被释放和重用掉,而不必等待整个堆中所有指向该 Region 的引用被修改后才能清理染色指针可以大幅减少在垃圾收集过程中内存屏障的使用数量,设置内存屏障,尤其是写屏障的目的通常是为了记录对象引用的变动情况,如果将这些信息直接维护在指针中,可以省去一些专门的记录操作染色指针可以作为一种可扩展的存储结构用来记录更多与对象标记、重定位过程相关的数据,有利于日后提升性能。
javac源代码编译器
即时编译器为了提高性能,JVM提供了JIT编译器。JIT编译器能够在运行时将热点代码(频繁执行的代码)编译成本地机器码,直接在硬件上运行,无需每次重新解释,从而提高效率。
(分代)垃圾回收算法
根可达算法(可达性分析):1、从GC Root遍历搜索,能被直接或间接引用到的对象称为可达,反之不可达。GC Root对象:1、类中的静态变量:方法区中的类静态属性引用的对象2、类中的final常量:方法区中常量引用的对象3、方法中局部变量:虚拟机栈(栈帧中的局部变量表)4、本地方法栈中JNI(Native方法)引用的对象5、Java虚拟机内部的引用,如基本数据类型对应的Class对象,一些常驻的异常对象(比NullPointExcepiton、OOM)等,还有系统类加载器6、所有被同步锁(synchronized关键字)持有的对象7、反映Java虚拟机内部情况的JMXBean、JVMTI中注册的回调、本地代码缓存等
操作数栈
(GC)垃圾收集
double
解释执行器Interpreter
老年代(2/3)MojorGC
执行引擎(Execution Engine)
自定义类加载器 隔离加载类、修改类加载的方式、扩展加载源、防止源码泄漏
内存泄露(memory leak):指程序中已分配的内存由于某种原因未能释放,导致这部分内存无法被再次使用,内存泄漏并不总是立即导致问题,但如果程序长时间运行,泄漏的内存量不断累积,最终可能会导致内存溢出。内存溢出(out of memory):指程序在申请内存时,没有足够的内存空间可分配,超过最大内存限制,出现out of memory。逃逸分析逃逸分析是JVM的一个优化手段,如果一个对象在方法中被创建,并且它的引用没有被传递到方法外部,那么这个对象就被认为是“不逃逸”的,反之则是“逃逸”的,逃逸分析默认是开启的,所以栈上分配、同步消除、标量替换都生效。如果一个对象是“不逃逸”的,那么计算机可以在内存中更有效地处理它,比如直接在栈上创建和销毁,这样就很快,也不需要额外的清理工作。如果一个对象是“逃逸”的,那么它可能会在很多地方被用到,所以计算机就需要在堆内存中处理它,这样的处理速度比栈慢,而且在不需要它的时候,还需要额外的工作来清理它。强引用:当存在对象引用时,即使内存不够OOM也不回收软引用:当存在对象引用时,内存不够用时回收弱引用:当存在对象引用时,每次GC回收虚引用:结合引用队列使用
线程安全问题
对象进入年老代条件:1、S区对象年龄达到15时(cms默认6岁)2、大对象直接进入(可设置)3、S区相同年龄的对象大小总和超过S区空间一半,大于等于该年龄的所有对象直接进入
卸载
注解语法树
线程共享区域
符号引用:类和结构的全限定名字段名称和描述符方法名称和描述符
Shenandoah
new 指令
Java堆
使用
并发标记: 并发标记是遍历对象图做可达性分析的阶段,前后也要经过类似于G1、Shenandoah的初始标记、最终标记的短暂停顿。ZGC 标记阶段会更新染色指针的 Marked 0、Marked 1 标记位并发预备重分配: 这个阶段需要根据特定的查询条件统计得出本次收集过程要清理哪些 Region,将这些Region组成重分配集(决定了分配集中存活对象会被重新复制到其他的 Region 中,里面的 Region 会释放)并发重分配:重分配集中的存活对象复制到新的Region上,并为重分配集中的每个Region维护一个转发表,记录从旧对象到新对象的转向关系(并发使用内存屏障实现指针 \"自愈\")。并发重映射(Concurrent Remap): 重映射所做的就是修正整个堆中指向重分配集中旧对象的所有引用
装载
几乎所有的对象实例以及数组都应当在堆上分配,因逃逸分析技术的日渐强大,以下两种情况不在堆上分配:一、 栈上分配 1. 每个线程在Eden区会分配一个称为TLAB(Thread-Local Allocation Buffer) 的私有缓冲区。对象分配时,优先尝试从该缓冲区\"指针+大小\"的方式快速分配,不需要加锁,避免了多个线程争抢同一分配指针,提高了吞吐和并发效率 2. 当TLAB不足以容纳对象,JVM会分配新的TLAB或者将对象直接放入共享的Eden区。共享Eden区的分配则通过CAS(Compare-And-Swap)或原子指令保线程安全二、 标量替换:JVM支持通过逃逸分析(Escape Analysis) 判断对象是否可以安全地在栈上分配。如果对象未被共享或逃逸,仅在方法内部使用,则可以被优化到栈上或消除,从而进一步降低堆分配和GC压力
ParNew 实质上是 Serial 收集器的多线程并行版本,除了同时使用多条线程进行垃圾收集之外,其余的行为包括 Serial 收集器可用的所有控制参数、收集算法、STW、对象分配规则、回收策略等都与 Serial 完全一致。除了 Serial 收集器之外,目前只有 ParNew 能与 CMS 收集器配合工作JDK 9 开始 ParNew + CSM 不再是官方推荐的服务端模式下的收集器解决方案
JVM参数常用参数注:建议将Xms和Xmx设为一样大,避免每次垃圾回收完成后JVM重新分配内存。如果虚拟机启动时设置的Xms比较小,这个时候又需要初始化很多对象,虚拟机会不断地增加内存。堆内存设置 -Xms<size>:(默认物理内存1/64)设置JVM启动时的初始堆大小。例如-Xms512m设置初始堆大小为512兆。 -Xmx<size>:(默认物理内存1/4)设置JVM可以使用的最大堆大小。例如-Xmx2048m设置最大堆大小为2048兆。 -Xmn<size>:设置年轻代的大小。年轻代是堆的一个子区域,专门用于存放新生成的对象。元空间(替代了永久代) -XX:MetaspaceSize=<size>:设置元空间的初始大小。 -XX:MaxMetaspaceSize=<size>:设置元空间的最大大小。垃圾收集器设置 -XX:+UseG1GC:启用G1垃圾收集器。 -XX:+UseConcMarkSweepGC:启用CMS(并发标记清除)垃圾收集器。 -XX:+UseParallelGC:启用并行垃圾收集器。 -XX:+UseSerialGC:启用串行垃圾收集器。性能调优 -XX:SurvivorRatio=<ratio>:设置Eden区与Survivor区的大小比例。 -XX:NewRatio=<ratio>:设置年轻代(包括Eden区和两个Survivor区)与老年代的比例。 -XX:MaxTenuringThreshold=<value>:设置对象晋升到老年代的年龄阈值。GC日志设置 -Xloggc:<file>:将GC日志输出到指定的文件。 -XX:+PrintGCDetails:在日志中输出详细的GC信息。 -XX:+PrintGCDateStamps:在GC日志中加入时间戳。系统属性 -D<property>=<value>:设置系统属性。例如-Djava.library.path=/usr/lib设置库路径。JDK 9+的统一JVM日志系统 -Xlog:<option>:JDK 9引入了新的统一日志系统,可以使用-Xlog来控制各种日志。其他 -XX:+HeapDumpOnOutOfMemoryError:在内存溢出时自动生成堆转储文件。 -XX:HeapDumpPath=<path>:设置堆转储文件的路径。 -XX:+DisableExplicitGC:禁止代码中显式调用System.gc()。
Parallel Scavenge 收集器的目标是达到一个可控的吞吐量吞吐量 = 用户运行代码时间 / (用户运行代码时间 + 运行垃圾收集时间)高吞吐量可以最高效率的利用处理器资源尽快完成运算任务,适合在后台运算而不需要太多交互的分析任务
Survivor(s1 To)每次交换后空的叫To区1/10
Java类加载的3中方式:1、双亲委派(父类委托): 当前类加载器需要加载类时不会自己直接去加载,而是尝试将加载这个类的任务向上传递交给父加载器去完成,如果父类加载器能加载则由父类加载器来完成而自己不会再去加载,从而保证类在虚拟机中的唯一性,避免重复加载。 打破:自定义类加载器重写loadClass方法、SPI(服务提供者接口)、OSGI(开放服务网关协议)。2、全盘委托(当前类加载机制):(仅仅是调用了loadClass方法,不负责加载Class文件生成Class对象)当一个classloader加载一个Class的时候,这个Class所依赖的和引用的所有Class也由这个classloader负责载入(除非是显式的使用另外一个classloader载入)。3、自缓存机制:所有加载过的Class存入cache(方法区),如果cache中存在这个Class就直接返回,如果没有才从文件中读取和转换成Class,并存入cache,这就是为什么我们修改了Class但是必须重新启动JVM才能生效的原因。
转发指针:在原有对象布局结构的最前面统一增加一个新的引用字段,在正常不处于并发移动的情况下,该引用指针指向对象自己转发指针加入后旧对象上的转发指针的引用位置,使其指向新的对象,便可将所有对该对象的访问转发到新的副本上转发指针访问的并发问题:Shenandoah 采用 CAS 来保证收集线程和用户线程只有一个能访问转发指针转发指针的执行频率问题:同时设置读、写屏障进行拦截
验证字节流信息是否符合jvm要求,是否给jvm带来安全问题 文件格式: 1)是否以16进制cafebabe开头 2)版本号是否正确 元数据(校验Java语法): 1)是否有父类 2)是否集成了final类 3)非抽象类是否实现了抽象方法 字节码(数据流与控制流的分析,校验对jvm的安全危害): 1)运行检查 2)栈数据类型和操作码操作参数是否吻合 符号引用: 1)常量池中描述类是否存在 2)访问的方法或者字段是否存在且具有足够权限
为类变量(静态变量,非实例变量)分配(方法区)内存,并分配初始值private static int a = 1; //该阶段为0private final static int a= 1; //该阶段为1,在类的字段表属性中存在一个特殊属性ConstantValue: int 1ConstantValue只能为基本类型及String初始化(常量池)
标记整理算法(老年代)
虚拟机栈1. 每个Java线程创建时都会创建一个虚拟机栈,这个栈是私有的。它包含了一系列的栈帧(Stack Frames),每个栈帧对应着一个Java方法的调用。2. 如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常;如果Java虚拟机栈容量可以动态扩展,当栈扩展时无法申请到足够的内存会抛OutOfMemoryError异常。
运行时数据
堆内存是否规整
空闲列表
小型 Region:容量固定为 2MB,用于放置小于 256K 的小对象中型 Region:容量固定为 32MB,用于放置大于等于 256KB 但小于 4MB 的对象大型 Region:容量不固定,可动态变化,但必须是 2MB 的整数倍,用于放置 4MB 或以上的大对象。每个大型 Region 中只会存放一个对象,实际容量可能小于中型 Region,最小容量可低至 4MB
锁状态标示01
新生代
根可达算法(可达性分析)
..........
数组长度(数组)
语法树
CAS+失败重试
Young
程序计数器1. 由于CPU执行指令是可中断的,会有线程切换,程序计数器会记录当前线程执行的字节码指令地址(行号),以便线程切换后能恢复到正确的执行位置。2. 如果是Native方法,则计数器值为空(Undefined)3. 唯一一个没有规定任何OOM情况的区域
连接
MinorGC?
Unused(18bit)
ZGC 堆内存布局
本地方法栈
标记 GC Roots 能直接关联的对象,并修改 TAMS 值,需要 STW
对象内存分配过程
Object Address(42bit,4TB address space)
Parallel Old
Shenandoah 的目标是实现一种在任何堆内存大小下都可以把垃圾收集的停顿时间限制在 10 毫秒以内,所以它不仅要进行并发的垃圾标记,还要并发的进行对象清理后的整理动作。Shenadoash 相对于 G1 的不同点: 1.支持并发的整理算法,可以实现回收阶段与用户线程并发执行; 2.Shenadoash 默认不使用分代收集,意味着不会有新生代 Region、老年代 Region; 3.摒弃了 G1 中的记忆集,改用 \"连接矩阵\" 的全局数据结构来记录跨 Region 的引用关系,降低了处理跨代指针时的记忆集维护消耗,降低了伪共享问题发生概率;
指向对象实例数据的指针
栈空间足够且非逃逸对象?
动态链接简单来说,动态链接是 JVM 在运行时解析那些被编译代码中的符号引用的机制。
引用指向实例 obj@6688
CMS 收集器是一种以获取最短回收停顿时间为目标的收集器(使用B/S上)CMS 三个缺点:CMS 收集器堆处理器资源非常敏感;由于 CMS 收集器无法处理浮动垃圾(其他线程产生垃圾,当次无法回收的垃圾),有可能会出现 \"Concurrent Mode Failure\" 失败进而导致另一次完全 STW 的 Full GC 产生;空间碎片过多,将会给大对象带来很大麻烦(内存不足);
标记清除算法(老年代)
语法分析器
用于出来并发阶段结束后遗留下来的 STAB 的记录,需要 STW
开启栈内分配 ?
Class对象信息(类元信息、属性、方法、静态变量等)
栈帧1. 每个方法被执行的时候,Java虚拟机都会创建一个栈帧,每个栈帧对应着一个java方法的调用2. 每一个方法被调用直至执行完毕的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程
本地接口通过本地接口,执行引擎可以调用本地库方法,用于执行不是用Java编写的代码。
本地方法库本地方法库是一组用本地(通常是C或C++)编程语言实现的方法的集合,本地方法库允许Java程序调用这些非Java方法,从而执行一些Java本身无法进行或者不方便进行的操作。
老年代和新生代
执行<init>方法
E
S
O
对象引用
0 条评论
下一页