理解版
为了更好的了解JVM的工作流程,接下来我们会用一个形象的故事来解释JVM的工作流程,你可以把JVM理解为一座现代化的图书馆,它负责管理一系列的图书(Java类和方法)。
1. 图书收集与整理(类加载与链接)
- 收集图书(加载):图书馆的工作人员(类加载器)从各地收集图书(.class文件),并将它们带到图书馆(JVM)中。
- 整理图书(链接):
- 检查图书(验证):工作人员检查图书是否有损坏,内容是否健康(确保类文件符合JVM规范)。
- 准备书架(准备):为每本图书分配一个特定的书架位置(为类变量分配内存),并贴上标签(创建Class对象)。
- 目录更新(解析):更新图书馆的电子目录系统,让读者(执行引擎)能够轻松找到每本图书(将符号引用转换为直接引用)。
2. 读者服务(执行准备)
- 图书上架(初始化):新到的图书需要先上架,工作人员会按照分类摆放好,并确保所有参考资料都已就绪(执行
<clinit>()
方法)。- 借阅服务(创建栈帧):每当读者(线程)想要阅读一本图书时,图书馆提供一个专门的阅读位(栈帧),让读者可以专注于阅读(存储局部变量和方法调用信息)。
3. 阅读时间(执行)
- 阅读指导(字节码执行):图书馆有专门的阅读指导员(执行引擎),他们指导读者如何阅读图书(解释字节码或编译执行)。
- 异常处理(异常处理):如果读者在阅读过程中遇到难以理解的部分(异常),图书馆提供辅导服务(
catch
块),帮助他们解决问题或寻找其他读者(调用者)帮助。- 多人阅读(并发控制):图书馆有多个阅读位,但某些热门图书(共享资源)可能需要排队等待(
synchronized
同步)。图书馆确保每次只有一个读者能够阅读特定的图书,避免混乱。
4. 清洁与维护(垃圾回收)
- 整理书架(标记):图书馆定期检查哪些图书长时间未被阅读(不可达对象),准备将它们下架。
- 清理空间(清除):将这些不再需要的图书清理出图书馆,为新到的图书腾出空间。
- 重新布局(压缩):为了更有效地利用空间,图书馆可能会重新整理书架,将图书紧密排列,更新目录信息。
5. 特殊服务(本地方法调用)
- 外文图书(JNI调用):图书馆还提供外文图书(本地方法)的阅读服务,通过特殊翻译(JNI)帮助读者理解这些图书。
6. 图书馆闭馆(程序退出)
- 当图书馆到了闭馆时间(程序结束),工作人员会确保所有读者离开阅读位,图书都已归还并放回原位,关闭所有灯光和设备。
7. 图书馆管理(监控与调试)
- 监控系统(性能监控):图书馆有一套监控系统,可以实时了解哪些图书受欢迎(性能监控),哪些阅读位被频繁使用(线程状态)。
- 咨询服务(调试):如果读者在阅读过程中遇到问题,图书馆提供咨询服务(调试工具),帮助他们找到问题并解决。
通过这个形象的比喻,我们可以看到JVM如何管理Java程序的整个生命周期,从类的加载到执行,再到资源的清理和程序的退出。异常处理和并发控制确保了程序的稳定运行,而垃圾回收机制则保持了JVM的高效和整洁。监控和调试工具则帮助开发者优化程序性能,确保程序能够顺畅地运行。
专业版
下面是JVM的常规的工作流程。
1. 类加载与链接(Class Loading and Linking)
加载(Loading):
- 类加载器从.class文件中读取二进制数据。
- 类加载器将类的静态结构(如字段、方法、继承信息)加载到JVM的方法区。
链接(Linking):
- 验证(Verification):
- 文件格式验证:确保.class文件符合JVM规范。
- 元数据验证:检查类的定义是否符合Java语言规范。
- 字节码验证:确保字节码操作是合法的,没有违反JVM指令集规则。
- 准备(Preparation):
- JVM为类变量分配内存,并设置默认初始值。
- 创建一个与类相关联的Class对象。
- 解析(Resolution):
- 将符号引用转换为直接引用。
- 包括解析类或接口的引用、字段引用、方法引用等。
2. 执行准备(Execution Preparation)
初始化(Initialization):
- 执行
<clinit>()
静态初始化块,为静态变量赋予初始值。- 静态初始化块只会在类首次被使用时执行一次。
创建栈帧(Stack Frame Creation):
- 每个线程的方法调用都会创建一个新的栈帧。
- 栈帧包含局部变量表、操作数栈、动态链接信息和方法返回信息。
3. 执行(Execution)
字节码执行(Bytecode Execution):
- 执行引擎解释或编译字节码,执行方法和指令。
- 执行引擎使用当前活动的栈帧来访问局部变量和操作数栈。
异常处理(Exception Handling):
- 当执行到可能抛出异常的代码时,JVM会检查是否有异常处理表中的匹配
catch
块。- 如果发生异常,控制流转移到匹配的
catch
块。- 如果异常没有被捕获,它会传播到调用者,可能导致线程终止或程序退出。
并发控制(Concurrency Control):
- 在多线程环境中,JVM通过
synchronized
关键字来提供基本的并发控制。synchronized
块或方法使用锁机制来保证同一时间只有一个线程可以执行特定的代码段。- JVM还提供了更高级的并发工具,如
java.util.concurrent
包中的类,用于构建复杂的并发程序。
4. 垃圾回收(Garbage Collection)
- 标记(Marking):
- 垃圾收集器识别哪些对象是可达的,哪些是不可达的。
- 通常从GC Roots开始,通过一系列称为“GC Roots”的对象(如活动线程、静态字段等)来追踪所有可达对象。
- 清除(Clearing):
- 不可达对象的内存被标记为可用,以便将来分配。
- 垃圾收集器在适当的时候回收这些标记的内存。
- 压缩(Compacting):
- 为了防止内存碎片化,垃圾收集器可能会移动对象,使它们紧密排列,然后更新所有引用以指向新的位置。
5. 本地方法调用(Native Method Invocation)
- JNI调用(JNI Invocation):
- 通过JNI,Java代码可以调用本地库中的方法,如用C/C++编写的代码。
- JNI运行时负责将Java对象和方法调用转换为本地代码的调用,并处理返回结果。
6. 程序退出(Program Exit)
- 资源清理(Resource Cleanup):
- 在程序退出前,JVM会尝试关闭所有资源,如文件句柄、网络连接等。
- 确保所有线程都被正确终止。
7. 监控与调试(Monitoring and Debugging)
- 性能监控(Performance Monitoring):
- 使用JVM提供的监控工具,如VisualVM、JConsole等,来监控程序的性能。
- 工具可以提供内存使用、线程状态、垃圾回收活动等信息。
- 调试(Debugging):
- 使用JDB(Java Debug Wire Protocol)或其他调试器来调试Java程序。
- 调试器允许开发者设置断点、检查变量值、单步执行代码等。