JVM 规范定义的是抽象执行模型,而不是操作系统内存映射。实现可以使用 JIT 编译、移动式垃圾回收、连续或分段存储以及本机元数据,同时保持规范所定义的行为。
运行时数据区 #
每个 JVM 线程都有程序计数器寄存器、JVM 栈,以及可选的本机方法栈。对于 Java 代码,PC 标识当前 JVM 指令;执行本机代码时,其值未定义。JVM 栈保存帧;创建帧失败时,达到大小限制会产生 StackOverflowError,可扩展栈无法增长时会产生 OutOfMemoryError。
堆由所有线程共享,其中包含对象和数组。方法区由所有线程共享,并保存每个类的结构;其具体位置(例如 HotSpot 元空间)由实现决定。每个类或接口都有一个运行时常量池,它是类文件常量和符号引用的运行时形式。这些共享区域通过类加载访问,并且与线程的帧分离。
帧和执行 #
每次方法调用都会创建一个帧,其中包含局部变量数组、操作数栈、对当前类运行时常量池的引用,以及返回或异常完成状态。类文件中的 max_locals 和 max_stack 限制前两个区域。字节码在局部变量和操作数栈之间移动值;调用指令解析符号引用,并将控制转移到新帧。正常完成时,结果返回到调用者的操作数栈;异常完成时,VM 会在展开过程中搜索异常处理器。
类加载器提供类文件字节,并建立类型身份。执行引擎验证、解释字节码,也可能编译字节码;它不是类加载器。JVMS 以加载、链接、初始化和指令执行来规定二者的边界,而不是规定某种引擎架构。
布尔值和退出 #
JVM 没有专用的布尔算术指令。Java 编译器对布尔表达式使用 int 计算值,并使用
baload 和 bastore 访问 boolean 数组;规范要求其存储编码使用 0 表示 false,
使用 1 表示 true。不要根据字节码约定推断 Java 对象字段布局。
Java 应用通常会在最后一个非守护线程结束时终止。System.exit 通过运行时请求 VM
终止,而一个线程未恢复的失败不会终止其他非守护线程。本机代码、关闭钩子和主机进程都可能影响观察到的关闭行为。
评论
在 GitHub 上阅读或评论(发表评论需要登录)