线程私有区域
程序计数器(Program Counter Register)
程序计数器是一块较小的内存空间,可以看作是当前线程所执行的字节码的行号指示器。每个线程都有一个独立的程序计数器,线程私有。如果线程正在执行Java方法,计数器记录的是正在执行的虚拟机字节码指令的地址;如果执行的是本地方法,计数器值为空(Undefined)。
主要作用
-
指示下一条指令地址:程序计数器存储的是当前线程正在执行的字节码指令的地址。它指向方法区中该线程正在执行的方法的字节码地址,确保了线程能够准确地执行下一条指令。
-
线程切换时保持状态:由于程序计数器是线程私有的,每个线程都有独立的程序计数器。这确保了在多线程环境下,线程切换后能够正确地恢复到上一个线程执行的位置。
-
不会发生内存溢出:程序计数器是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。它只是一个计数器,不会进行内存分配,因此不存在内存溢出的问题。
生命周期
-
线程创建时初始化:每个线程创建时,程序计数器会被初始化为0。这是线程刚刚启动时,程序计数器所处的初始状态。
-
方法调用和返回:在方法调用时,程序计数器会记录调用位置的字节码地址。在方法返回时,它会恢复到上一个方法的调用位置。这保证了方法的顺利执行和返回。
-
线程切换:线程切换时,当前线程的程序计数器值会被保存到线程私有的内存区域,切换到下一个线程时,程序计数器值会从线程私有的内存区域中恢复。
Java虚拟机栈(Java Virtual Machine Stacks)
虚拟机栈是线程私有的,生命周期与线程相同。每个方法执行时会创建一个栈帧(Stack Frame),用于存储局部变量表、操作数栈、动态链接、方法出口等信息。局部变量表存放编译期可知的基本数据类型、对象引用和返回地址。栈深度超过虚拟机允许的深度时会抛出StackOverflowError;如果栈可以动态扩展但无法申请足够内存时会抛出OutOfMemoryError。
本地方法栈(Native Method Stack)
本地方法栈与虚拟机栈作用类似,区别在于虚拟机栈为执行Java方法服务,而本地方法栈为执行本地(Native)方法服务。有的虚拟机(如HotSpot)将两者合并。同样会抛出StackOverflowError和OutOfMemoryError。
线程共享区域
Java堆(Java Heap)
堆是虚拟机管理的最大一块内存,被所有线程共享,在虚拟机启动时创建。唯一目的是存放对象实例,几乎所有对象实例都在堆上分配内存(随着逃逸分析技术成熟,也可能在栈上分配)。堆是垃圾收集器管理的主要区域,因此也称为“GC堆”。从内存回收角度看可分为新生代、老年代等;从内存分配角度看可能存在多个线程私有的分配缓冲区(TLAB)。堆可以处于物理上不连续但逻辑上连续的空间中,扩展时抛出OutOfMemoryError。
方法区(Method Area)
方法区是线程共享的内存区域,存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。在HotSpot虚拟机中也被称为“永久代”(JDK 8前),后改为元空间(Metaspace)并使用本地内存实现。方法区逻辑上是堆的一部分,但规范中要求与堆区分。溢出时抛出OutOfMemoryError。
运行时常量池(Runtime Constant Pool)
运行时常量池是方法区的一部分,存放编译期生成的字面量和符号引用。Class文件中除了有类的版本、字段、方法等描述信息外,还有常量池表(Constant Pool Table),用于存放编译期生成的字面量和符号引用,这部分内容在类加载后存放到运行时常量池中。动态性支持如String.intern()方法。溢出时抛出OutOfMemoryError。
补充说明
直接内存(Direct Memory)
直接内存并非虚拟机运行时数据区的一部分,但频繁使用(如NIO的ByteBuffer.allocateDirect)。这部分内存直接通过本地方法库分配,不受Java堆大小限制,但受本机总内存限制。配置时忽略直接内存可能导致OutOfMemoryError。
注意:不同虚拟机实现可能有差异,以上描述基于Java虚拟机规范及HotSpot虚拟机的主流行为。
1474

被折叠的 条评论
为什么被折叠?



