Java执行流程(含JVM)

1、开始
从创建HelloWorld.java源代码文件开始
点击查看代码
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
2、javac编译器
通过使用javac编译器
输入HelloWorld.java文件
输出HelloWorld.class文件
3、JVM-类加载(Class Loading)
JVM的类加载器将 HelloWorld.class文件 加载到 内存中
3.1、类加载器类型
- 自定义加 Class Loader
- Application Class Loader 应用程序类加载器
- Extension Class Loader 扩展类加载器
- BootStrap Class Loader 启动类加载器
3.2、双亲委派机制
核心:
类加载器在加载 HelloWorld.class时;
先向上委派给父类去加载,父类加载器再向上委派给它的父类,直到根类加载器,父类加载器无法加载时,才由当前类加载器自行加载
安全性:
避免重复加载类。例如java.lang.Object类只能由根类加载器加载,防止恶意代码加载不受信任的类来替代系统核心类
一致性:
保证同一个类在JVM中只会被加载一次,确保整个应用使用中是同一个类对象
4、JVM-方法区(Method Area)
存储内容:类的元数据:类民、父类、接口、方法代码、常量池
HelloWorld类中main()字节码、System.out的符号引用存储在方法区
常量池存储"Hello, World!"字符串字面量
5、JVM-堆(Heap)
存储内容:存储对象的实例和数组;
main()执行时,String对象"Hello, World!"在堆中创建
6、JVM- Java栈(Stack)
存储内容:每个方法的栈帧(局部变量表、操作数栈、动态连接、返回地址)
main()的栈帧:
- 局部变量表:
args - 操作数栈:暂存
System.out对象和字符串"Hello, World!" - 动态连接:将
System.out.println符号引用解析为直接地址
7、JVM-本地方法栈(Native Method Stack)
作用:支持native方法(如C/C++实现的方法)调用
例:System.out.println最终调用native方法(如write())将数据输出到控制台,本地方法栈管理这些调用
8、JVM-程序计数器
作用:记录当前线程执行的字节码指令地址
例:线程执行main()时,程序计数器指向当前执行的指令(如invokevirtual调用println)
9、JVM-执行引擎
main首次执行时,由解释器逐行解释
若main方法多次被调用(如其他代码重复执行),JIT会将其编译为机器码加速执行
9.1、解释执行
是指源代码不经过编译器的预先编译,而实在运行时通过解释器逐行翻译并执行。
优点:跨平台性好,因为代码在每个平台上都是通过相应平台运行的,且开发周期更短
缺点:运行速度慢,因为每次执行时都需要进行动态翻译和解释
9.2、编译执行
是指程序在执行之前,首先通过编译器将源代码编译为机器吗,然后直接在CPU上运行
优点:编译后的程序运行速度快,因为机器码是针对目标平台直接生成,且不需要在运行时再进行翻译
缺点:程序针对每个平台重新编译,跨平台性差;编译后的机器代码难以调试和逆向工程
9.3、JVM的执行方式
JVM采用编译执行和解释执行相结合的方式
解释执行:JVM会逐行解释执行字节码,尤其是程序首次运行时,这种方式有助于程序的跨平台性
即时编译-JITJVM引入了即时编译器(Just-In-Time Compiler),在程序运行时将 经常执行的代码(热代码)编译为本地机器吗,避免反复解释(即减少解释执行的开销),提升性能
9.4、JIT编译的类型
C1(Client Compiler):用于快速启动的轻量级优化,适用于客户端应用程序
C2(Server Compiler):用于长时间运行的重度优化,适用于服务器端应用程序
9.5、JIT调优
JVM提供了多种参数用于调优JIT编译器,如:
-XX:+PrintComPilaction用于输出编译信息
-XX:TieredStopAtLevel控制JIT编译级别
通过调优,可以在启动时间和运行性能之间取平衡
10、JVM-本地方法接口(JNI)和本地方法库
JNI(Java Native Interface):提供调用操作系统底层功能(如文件、网络IO)的接口
本地方法库:包含操作系统相关的.dll(Windows)或.so(Linux)库
如:System.out.println最终通过JNI调用本地库的write函数输出到终端
11、JVM-垃圾回收
Who?谁负责执行回收
JVM内置的垃圾回收器(Garbage Collector GC)
Serial GC:单线程,适合客户端应用
Parallel GC:(吞吐量优先)多线程并行回收
CMS GC:(低延迟)并发标记清除,减少停顿时间
G1 GC:(区域化分代)平衡 吞吐量与延迟
ZGC/Shenandoah:(超低延迟)支持TB级别堆内存,停顿时间<10ms
What?垃圾回收是什么
JVM自动管理堆内存的机制,回收不在被引用的对象,释放内存空间
核心任务:
- 标记:识别存活对象
- 清除:回收垃圾对象占用的内存
- 整理:消除内存碎片,提升内存连续性
When?何时回收
新生代:当
Eden区空间不足 或Eden和Survivor区都满了,均不足以存放新对象时,触发Young GC,清理空间并将幸存的对象转移到Survivor区或晋升到老年代
Survivor 0和Survivor 1区之间有 from 和 to 之分(谁空谁为to的原则区分);
通过复制算法,进行对Eden和Survivor区(即from)整片区域复制到to中去;
老年代:当老年代空间不足(新生代晋升到老年代的对象过多 或 大的对象直接分配失败);
针对老年代执行Old GC(Major或OGC)
Full GC 全堆垃圾回收;是对新生代和老年代的整个堆内存进行回收
触发条件:
老年代空间不足且无法通过Old GC(Major或OGC)释放空间
永久代或元空间空间不足
显示调用 System.gc(),JVM可能会触发执行一次
新生代晋升到老年代失败,新生代中大的对象或者长期存活的对象,晋升老年代时,空间不足会触发
在young GC时,根据之前数据统计发现年轻代平均晋升大小比现在老年代剩余空间还大,触发
或者其他情况导致系统内存压力较大时
永久代/元空间:类卸载或元空间占用超过阈值时触发
Where?在哪回收:
Heap 堆中(新生代和老年代);Method Area(JDK8 为元空间)回收废弃常量和类
Why?为什么回收:
避免内存泄露(自动释放无用对象,防止内存耗尽)
提升开发效率(无需手动管理)
优化内存使用(通过分代回收策略,平衡吞吐量与延迟)
保障程序稳定性(防止OOM)
How?怎么回收
分代回收策略:新生代(复制算法);老年代(标记清除/整理算法)
12、结束
main()结束、JVM清理资源,退出程序

浙公网安备 33010602011771号