JVM细节拆截导图(面试必备)
2025-10-20 12:35:48 0 举报
JVM细节拆截导图(面试必备)
作者其他创作
大纲/内容
TLAB(线程私有)
进行加载
虚拟机完成类装载过程后,将class文件中的常量池载入
Java源码
可能会发生,需要同时满足3种情况:1、该类所有的实例都已经被回收,堆中不存在该类的任何实例2、加载该类的ClassLoader已经被回收3、该类对应的java.lang.Class对象没有被引用,无法通过反射访问该类的方法
G1(并发)整理+复制
CMS(并发)清除
JVM启动时创建并与JVM进程绑定,是堆的逻辑部分,内存不足时会抛出OOM异常
MarkWord
字节码文件加载途径:1、从本地系统直接加载2、通过网络下载.class文件3、从归档文件中加载.class文件(jar、war、zip)4、从专有数据库中提取.class文件5、将Java源文件动态编译为.class文件(运行时计算而成:动态代理)6、从加密文件中获取
字节码生成器
垃圾判断算法
实例通过Class模板创建而来
验证
对象
ParallelScavenge(并行)复制
字面量:文本字符串final常量值基本数据类型值其他
栈帧
堆(Heap)FullGC
本地方法库Native Libraries
对齐填充-8字节倍数
Serial(串行)复制
语义分析器
Old
本地方法栈这个区域与虚拟机栈类似,但它是为JVM使用到的Native方法服务的。在执行Native方法时,这些方法的栈帧会被分配在本地方法栈上。
新生代(1/3)MinorGC
装载步骤:1. 通过全限定名获取class字节码文件二进制字节流2. 将静态结构载入方法区转换为运行时数据结构(类信息、静态变量、常量,即时编译的热点代码不在这个阶段进入方法区)3.在堆中生成一个代表该类的java.lang.Class对象(作为存储在方法区中对应数据的访问入口)注:在装载阶段可以对类加载器进行操作,还可以对字节码进行增强操作
运行时常量池
返回地址类型
垃圾收集(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 内部调优失败等。 操作:在 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)。GC的次数很频繁怎么办?几个维度: 1、Minor gc:可能young区空间不够,或eden、s0、s1比例分配不合理。 2、Major gc:old区空间不够,如young区不断有存活对象晋升到old区。频繁有大对象被分配到old区。 3、metaspace空间不够。
程序计数器
字节码
引用计数
本地接口Native Interface
编译结果(对应平台可执行文件)
准备
class字节码文件
age=15
Eden区(8/10)
虚拟机栈
初始化
局部变量表
垃圾收集器(连线代表可搭配使用)
字符串常量池
方法出口(也称为方法返回地址)在方法执行完毕后起到了控制程序流程的作用。1、返回到方法调用者2、恢复调用者的栈帧状态3、传递方法返回值4、异常处理
解释执行器Java程序开始运行时,解释器负责读取字节码指令,逐条读取逐条执行。这种方式效率不高,因为每次执行时都需要解释指令。
Survivor(s0 From)1/10
Token流
CodeCacheJIT编译代码产物
方法出口
JIT Compiler即时编译器
对象头
javac源代码编译器
即时编译器为了提高性能,JVM提供了JIT编译器。JIT编译器能够在运行时将热点代码(频繁执行的代码)编译成本地机器码,直接在硬件上运行,无需每次重新解释,从而提高效率。
(分代)垃圾回收算法
根可达算法(可达性分析):1、从GC Root遍历搜索,能被直接或间接引用到的对象称为可达,反之不可达。GC Root对象:1、类中的静态变量:方法区中的类静态属性引用的对象2、类中的final常量:方法区中常量引用的对象3、方法中局部变量:虚拟机栈(栈帧中的局部变量表)4、本地方法栈中JNI(Native方法)引用的对象
操作数栈
1、常量池符号引用替换为直接引用同一个符号可能被多次解析,除了invokedynamic指令,jvm会对第一次解析结果进行缓存2、“解析”有可能发生在“初始化”后(运行时绑定/晚期绑定:在运行时根据对象具体的类型进行绑定)
(GC)垃圾收集
解释执行器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区空间一半,大于等于该年龄的所有对象直接进入
卸载
注解语法树
线程共享区域
符号引用:类和结构的全限定名字段名称和描述符方法名称和描述符
垃圾回收算法标记清除算法(老年代):标记可达对象,清除未被标记的对象,会产生内存碎片,导致内存空间的不连续,后续可能更频繁的GC。标记整理算法(老年代):标记可达对象进行整理,整理碎片有性能开销标记复制算法(年轻代):需要开辟一块备用空间来处理GC操作内存效率:复制算法>标记清除算法>标记整理算法(非绝对)内存整齐度:复制算法=标记整理算法>标记清除算法内存利用率:标记整理算法=标记清除算法>复制算法哪种垃圾回收算法最好?各有长短,正所谓分代垃圾回收算法,不同的区域使用不同的算法回收垃圾对象。
ParNew(并行)复制
来回替换同一时刻永远有一个空的
使用
装载
词法分析器
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()。
类型指针
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才能生效的原因。
验证字节流信息是否符合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初始化(常量池)
实例数据
标记整理算法(老年代)
虚拟机栈每个Java线程创建时都会创建一个虚拟机栈,这个栈是私有的。它包含了一系列的栈帧(Stack Frames),每个栈帧对应着一个Java方法的调用。
Application(app)类加载器(派生于ClassLoader类)负责加载环境变量classpath或系统属性java.class.path指定路径下的类库
数组
动态链接
根可达算法(可达性分析)
..........
Serial Old(串行)整理
数组长度(数组)
语法树
Young
程序计数器由于CPU执行指令是可中断的,会有线程切换,程序计数器会记录当前线程执行的字节码指令地址(行号),以便线程切换后能恢复到正确的执行位置。如果是Native方法,则计数器值为空(Undefined)
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异常
本地方法栈
JVM运行时数据区
Extension(EXT)类加载器(派生于ClassLoader类)负责加载扩展jar包
标记复制算法(年轻代)
动态链接简单来说,动态链接是 JVM 在运行时解析那些被编译代码中的符号引用的机制。
线程私有区域
引用指向实例 obj@6688
Parallel Old(并行)整理
标记清除算法(老年代)
语法分析器
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)
Class对象信息(类元信息、属性、方法、静态变量等)
栈帧每个栈帧对应着一个java方法的调用
本地接口通过本地接口,执行引擎可以调用本地库方法,用于执行不是用Java编写的代码。
本地方法库本地方法库是一组用本地(通常是C或C++)编程语言实现的方法的集合,本地方法库允许Java程序调用这些非Java方法,从而执行一些Java本身无法进行或者不方便进行的操作。
...
对象引用
0 条评论
下一页