JVM Specification -Run-Time Data

2019-08-19  本文已影响0人  Popker

JVM规范 - JVM运行时数据区域(每天做点翻译1)

https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html

每天做点翻译第一篇。该片翻译了JVM在运行时的数据分区,并标注了在部分小节文后有标注来加深自己的理解。

The pc Register(pc寄存器/程序计数器)

JVM可以支持多个线程同时运行,每一个JVM线程都有自己的pc(program counter 程序计数)寄存器。在任何时刻,每个JVM线程只执行一个方法的代码,这个方法即是该线程的当前方法(current method)。如果当前方法不是native的,该线程的The pc Register有当前被执行的指令的地址;如果当前方法是native的,该线程的the pc Register的值就是undefined。 JVM的PC register足够容纳returnAddress 或者 在运行平台上的原生指针。

注:https://www.zhihu.com/question/53822079中解释了

PC是program counter的意思。它永远不会用来存储“当前指令”,而只是会存储代表当前程序位置的计数器——可以看作一个整数,也可以看作一个代码指针。

这里的returnAddress并不是返回后应该执行的指令的“地址”,而是而是纯粹用来存储指向Java字节码指令地址的类型。这个类型用于实现jsr(jump-to-subroutine)及其对应的ret指令,这俩指令(外加宽版的jsr_w)在JDK1.4.2之前用于实现finally块,而从Java SE 7开始就被禁用了。

Java方法调用的返回地址确实是存在抽象意义上的Frame上的。


JVM Stacks(JVM栈)

每个Java线程都又自己的JVM栈,当线程创建时一同被创建。JVM栈包含一些栈帧。JVM栈类似于诸如C的传统语言的堆栈:它持有了本地变量和局部结果,并且在方法的调用和返回上起到作用。因为JVM栈除了压栈和出栈外不会被直接操作,所以栈帧也可能在堆上被分配(物理上)。JVM栈的内存区域不需要是连续的

在第一版的The Java Virtual Machine Specification中,JVM栈( Java Virtual Machine Statcks)被称做Java栈(Java Stacks)

JVM规范允许JVM是固定大小或是大小根据需要动态扩展。但如果JVM栈是固定大小的,JVM栈的大小需要在创建的时候单独选择。

JVM的实现需要提供给编写程序的人或者用户提供初始化JVM栈大小的方法,或者在动态扩容的JVM栈实现中,提供给用户最大和最小值的设置

JVM栈与以下异常有关:

注1:https://www.zhihu.com/question/29833675/answer/82661572中解释了

JVM规范让每个Java线程拥有自己的独立的JVM栈,也就是Java方法的调用栈。同时JVM规范为了允许native代码可以调用Java代码,以及允许Java代码调用native方法,还规定每个Java线程拥有自己的独立的native方法栈。

这俩都是JVM规范所规定的概念上的东西,并不是说具体的JVM实现真的要给每个Java线程开两个独立的栈。以Oracle JDK / OpenJDK的HotSpot VM为例,它使用所谓的“mixed stack”——在同一个调用栈里存放Java方法的栈帧与native方法的栈帧,所以每个Java线程其实只有一个调用栈,融合了JVM规范的JVM栈与native方法栈这俩概念。


注2:该JVM Specification(JSE 8 Edition)有个自相矛盾的点:

2.5.2 JVM Stacks的章节中关于栈帧(Frame)的描述:

因为JVM栈除了压栈和出栈外不会被直接操作,所以栈帧也可能在堆上被分配

"Because the JVM stack is never manipulated directly except to push and pop frames, frames may be heap allocated."

2.6: Frames栈帧又描述:

帧是从创建帧的线程的JVM栈分配的。

"Frames are allocated from the JVM stack of the thread creating the frame."

目前我的理解是:栈是抽象的概念,栈上的帧也是。帧在逻辑上肯定归属于栈,但具体内存分配可以是在堆或其他内存区域


堆(Heap)

JVM有一个被所有JVM线程共享的叫做堆(Heap,注意这个堆只是指一块内存区域,并不是数据结构上的堆)。堆是运行时给所有类的实例和数组分配内存的区域。

堆在JVM启动时创建,堆给对象分配内存由垃圾回收器(garbage collector)管理,对象永远不会被显式回收。JVM没有规定必须使用那种类型的垃圾回收器,可以根据实现者的系统要求来选择合适的垃圾回收器。堆的大小既可以是固定的,也可以是根据计算扩大或者缩小。堆的内存区域同样不需要是连续的

Java虚拟机实现可以为程序员或用户提供对堆的初始大小的控制,或者如果堆可以动态的收缩和扩展,可以提供给程序员或用户控制扩展的最大值和收缩的最小值。

堆和以下异常有关:

注: 同样 https://www.zhihu.com/question/29833675/answer/82661572中解释了:

这个“堆”并不是数据结构意义上的堆(Heap (data structure),一种有序的树),而是动态内存分配意义上的堆——用于管理动态生命周期的内存区域。

JVM的堆被同一个JVM实例中的所有Java线程共享。它通常由某种自动内存管理机制所管理,这种机制通常叫做“垃圾回收”(garbage collection,GC)。JVM规范并不强制要求JVM实现采用哪种GC算法。


方法区(Method Area)

JVM的方法区被所有JVM线程共有。方法区类似于传统语言的编译代码的存储区域或类似于操作系统进程中的“文本”段("text" segment,也称作code segment,存储可执行指令)。方法区存储了每个类的结构,例如运行时常量池、方法参数和方法数据,还有方法和构造器(包括用于类和示例初始化以及接口初始化的special methods,special method主要有编译后的<init>等等)编译后的代码。

JVM方法去在JVM启动时被创建。虽然方法区逻辑上是堆heap的一部分,但简单的JVM实现可以选择不进行对它垃圾回收或者压缩。本规范不会规定方法区域的位置或用于管理编译代码的方法。和堆一样,方法去的大小既可以是固定的,也可以是根据计算扩大或者缩小。堆的内存区域同样不需要是连续的

Java虚拟机实现可以为程序员或用户提供对方法区的初始大小的控制,或者如果方法区可以动态的收缩和扩展,可以提供给程序员或用户控制扩展的最大值和收缩的最小值。

方法区和以下异常有关:


运行时常量池(Run-Time Constant Pool)

运行时常量池是每一个接口和类的.class文件中constant_pool table在运行时的表示,它包含几种常量,从编译时已知的数字文字(numeric literals)到必须在运行时解析的方法和字段引用。运行时常量池提供类似于传统编程语言的符号表的功能,尽管它包含比典型符号表更宽范围的数据。

每个运行时常量池是从JVM方法区分配出来的,每一个接口或者类的运行时常量池在这个类或接口被JVM生成时(编译期)被构建。

运行时常量池和以下异常有关:


本地方法栈(Native Method Stacks)

Java虚拟机的实现可以使用传统的堆栈,俗称“C堆栈”,来支持native的方法(只不是用Java语言编写的方法)。本地方法栈也可以通过用诸如C语言的Java虚拟机的指令集的解释器的实现来使用。如果JVM的实现无法加载本机方法且本身不依赖于传统堆栈,那么无需提供支持本地方法栈,如果提供了,本地方法栈则通常将在每个线程被创建时被分配

该规范允许本地方法栈是固定大小或者动态扩展的。如果本地方法栈是动态扩展的,每个本地方法栈的大小可以是在本地方法栈被创建时独立选择的。

Java虚拟机实现可以为程序员或用户提供对本地方法栈的初始大小的控制,或者如果本地方法栈可以动态的收缩和扩展,可以提供给程序员或用户控制扩展的最大值和收缩的最小值。

本地方法栈与以下异常有关:

上一篇下一篇

猜你喜欢

热点阅读