技术文章

Java运行时区域,哪些区域是线程私有的?哪些是共有的?

Java虚拟机(JVM)定义了在程序执行期间使用的各种运行时数据区域,这些JVM数据区域中的某些区域是按线程创建的,其他区域则是在JVM启动时创建的,并且内存区域在线程之间共享。

Java运行

根据使用情况,JVM运行时数据区域可分为六个区域:

程序计数器
虚拟机栈
本地方法栈

方法区
运行时常量池  
Java运行

如上所述,这些存储区域可以分为两类:

线程私有(程序计数器,虚拟机栈,本机方法栈) 线程共享(堆,方法区,运行时常量池) 程序计数器

在JVM中,在任何给定时间,可能正在执行许多线程。每个执行线程都有自己的PC寄存器。

如果JVM线程执行的方法是JAVA方法,则PC寄存器包含当前正在执行的Java虚拟机指令的地址。如果线程正在执行本机方法,则Java虚拟机的pc寄存器的值未定义。

虚拟机栈

每个JVM线程都有自己的JVM堆栈,该堆栈在线程启动时创建。JVM堆栈存储被推入堆栈并从堆栈弹出的框架,而JVM堆栈永远不会被直接操纵。在发生任何异常时,您都可以通过此堆栈跟踪获取每个元素代表一个堆栈框架的位置。

与Java虚拟机堆栈相关的特殊条件:

如果线程中的计算需要比允许的Java虚拟机更大的堆栈,则Java虚拟机将抛出StackOverflowError。 如果可以动态扩展Java虚拟机堆栈,并且尝试进行扩展,但是可以提供足够的内存来实现扩展,或者如果没有足够的内存来为新线程创建初始Java虚拟机堆栈,则Java虚拟机机器抛出OutOfMemoryError。  

虚拟机栈中的框架

调用方法时会创建一个新框架,然后将该框架推入该线程的JVM堆栈中。当框架的方法调用完成时,该框架将被销毁。

每个框架都有自己的局部变量数组,自己的操作数堆栈以及对当前方法类的运行时常量池的引用。局部变量数组和操作数堆栈的大小在编译时确定,并与与帧关联的方法的代码一起提供。

在任何时候,只有一个帧处于活动状态,这是执行方法的帧。该帧称为当前帧,其方法称为当前方法。定义当前方法的类是当前类。

请注意,由线程创建的框架在该线程本地,并且不能被任何其他线程引用。

局部变量 -创建并添加到JVM堆栈的每个框架都包含一个称为其局部变量的变量数组。局部变量数组的长度在编译时自行确定,并以类或接口的二进制表示形式以及与框架关联的方法的代码提供。 操作数堆栈 –每个帧都包含一个称为帧的操作数堆栈的后进先出(LIFO)堆栈。操作数堆栈的最大深度称为编译时间本身,并与与帧关联的方法的代码一起提供。 执行动态链接 -在已编译的.class文件中,方法的代码是指要调用的方法和要通过符号引用访问的变量。这些符号方法引用通过动态链接转换为具体的方法引用,并根据需要加载类以解析符号在那个时候还没有定义。   本地方法栈

JVM也可以使用常规堆栈来支持本地方法,本地方法是用Java编程语言以外的其他语言编写的方法。创建每个线程时,将为每个线程分配本地方法栈。

以下异常条件与本机方法堆栈相关联:

如果线程中的计算所需的本机方法堆栈超出允许的范围,则Java虚拟机将引发StackOverflowError。 如果可以动态扩展本机方法堆栈并尝试进行本机方法堆栈扩展,但可以提供足够的内存,或者可以提供足够的内存来为新线程创建初始本机方法堆栈,则Java虚拟机将引发OutOfMemoryError。
  堆

堆是JVM运行时数据区域,从中可以将内存分配给对象,实例变量和数组。堆是在JVM启动时创建的,并在所有Java虚拟机线程之间共享。一旦存储在堆中的对象没有任何引用,该对象的内存就会被垃圾收集器回收,该垃圾收集器是一种自动存储管理系统。对象永远不会显式释放。

以下异常情况与堆相关联:

如果计算需要的堆多于自动存储管理系统可以提供的堆,则Java虚拟机将引发OutOfMemoryError。
方法区

JVM具有在所有JVM线程之间共享的方法区域。方法区域存储有关已加载的类和接口的元数据。它存储每个类的结构,例如运行时常量池,字段和方法数据,以及方法和构造函数的代码。

对于JVM加载的每种类型,存储在方法区域中的类型信息如下-

类/接口的全限定名称。
任何直接超类的完全限定名称。
使用的修饰符。
任何扩展超级接口的全限定名称。
区分加载类型是类还是接口的信息。  

除类型信息方法区域外,还存储:

运行时常量池。
字段信息,包括字段名称,类型,修饰符。
方法信息,包括方法名称,修饰符,返回类型,参数。
静态(类)变量。
方法代码,包含字节码,局部变量大小,操作数堆栈大小。  

方法区域通常是非堆存储器的一部分,以前被称为PermGen空间。注意, PermGen Space已从Java 8更改为MetaSpace。

以下异常条件与方法区域相关联:

如果无法使方法区域中的内存可用以满足分配请求,则Java虚拟机将抛出OutOfMemoryError。   运行时常量池

运行时常量池是该类的constant_pool表的每个类或每个接口存储。Constant_pool包含在编译时已知的常量(字符串文字,数字文字),它还存储必须在运行时解析的方法和字段引用。

运行时常量池在线程之间共享,并从JVM的方法区域分配。

Java运行

不是将所有内容存储在字节码中,而是为该类维护单独的常量池,并且字节码包含对常量池的引用。这些符号参考通过动态链接转换为具体的方法参考。