Java虚拟机 —— 运行时数据区

Java虚拟机内存,是指JVM的运行时数据区域,主要分为:方法区、堆、虚拟机栈、本地方法栈、程序计数器。其中方法区和堆为索引线程的共享数据区,而虚拟机栈、本地方法栈、程序计数器为线程隔离的数据区。


程序计数器

每个线程都有一个独立的计数器用来记录程序当前执行的指令,可以看成是当前线程所执行的字节码的行号指示器。如果线程正在执行Java方法,计数器记录的是正在执行的虚拟机字节码指令的地址;如果执行的是Native方法,计数器记录值为空(Undefined)。程序计数器占用的内存空间非常小,是线程的私有区域,此内存区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。

虚拟机栈

虚拟机栈也是线程私有的,它的生命周期与线程相同。虚拟机栈是一个后进先出的数据结构,里面存放的是栈帧,每个Java方法的调用对应一个栈帧在虚拟机栈中的入栈和出栈。当线程执行一个Java方法执行时,就会创建一个新的栈帧并压入到该线程的虚拟机栈的栈顶,Java方法执行结束后栈顶的该栈帧就会弹出栈并销毁。

栈帧里面存放的是Java方法执行的一些数据,包括局部变量表、操作数栈、动态连接、方法出口等。


局部变量表

局部变量表是一组变量值存储空间,用于存放方法参数和方法内部定义的局部变量。

局部变量表的容量以变量槽(Slot)为最小单位,32位虚拟机中一个Slot可以存放一个32位以内的数据类型(boolean、byte、char、short、int、float、reference和returnAddress八种)。reference类型虚拟机规范没有明确说明它的长度,但一般来说,虚拟机实现至少都应当能从此引用中直接或者间接地查找到对象在Java堆中的起始地址索引和方法区中的对象类型数据。returnAddress类型是为字节码指令jsr、jsr_w和ret服务的,它指向了一条字节码指令的地址。

Java虚拟机是使用局部变量表完成参数值到Java方法参数变量列表的传递过程的,如果是实例方法(非static),那么局部变量表的第0位索引的Slot默认是用于传递方法所属对象实例的引用,在方法中通过this访问。

Slot是可以重用的,下一次分配Slot的时候,将会覆盖原来的数据。Slot对对象的引用会影响GC(要是被引用,将不会被回收)。

操作数栈

操作数栈也常被称为操作栈,同样是一个后进先出的数据结构。当一个方法刚刚开始执行的时候,这个方法的操作数栈是空的,在方法的执行过程中,会有各种字节码指令向操作数栈中写入和提取内容,也就是入栈出栈操作。在做算术运算的时候是通过操作数栈来进行的,在调用其他方法的时候是通过操作数栈来进行参数传递的。JVM将操作数栈作为工作区。JVM没有寄存器,所有的参数传递和返回值都是基于操作数栈来完成的。

比如,执行引擎执行c = a + b时,会先被操作的参数ab压入操作数栈,然后操作指令将他们弹出栈,并执行操作,将结果再压入栈。

Java虚拟机的解释执行引擎称为“基于栈的执行引擎”,其中所指的“栈”就是操作数栈。

动态连接

每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态连接。Class文件的常量池有存有大量的符号引用,字节码中的方法调用指令就以常量池中指向方法的符号引用为参数。这些符号引用一部分会在类加载阶段或第一次使用的时候转化为直接引用,这种转化称为静态解析。另外一部分将在每一次的运行期间转化为直接引用,这部分称为动态连接。

方法出口(返回地址)

当一个方法被执行后,有两种方式退出这个方法。第一种方式是执行引擎遇到任意一个方法返回的字节码指令,这时候可能会有返回值传递给上层的方法调用者(调用当前方法的方法称为调用者),是否有返回值和返回值的类型将根据遇到何种方法返回指令来决定,这种退出方法的方式称为正常完成出口(Normal Method Invocation Completion)。

另外一种退出方式是,在方法执行过程中遇到了异常,并且这个异常没有在方法体内得到处理,无论是Java虚拟机内部产生的异常,还是代码中使用athrow字节码指令产生的异常,只要在本方法的异常表中没有搜索到匹配的异常处理器,就会导致方法退出,这种退出方法的方式称为异常完成出口(Abrupt Method Invocation Completion)。一个方法使用异常完成出口的方式退出,是不会给它的上层调用者产生任何返回值的。

无论采用何种退出方式,在方法退出之后,都需要返回到方法被调用的位置,程序才能继续执行,方法返回时可能需要在栈帧中保存一些信息,用来帮助恢复它的上层方法的执行状态。一般来说,方法正常退出时,调用者的PC计数器的值就可以作为返回地址,栈帧中很可能会保存这个计数器值。而方法异常退出时,返回地址是要通过异常处理器来确定的,栈帧中一般不会保存这部分信息。

方法退出的过程实际上等同于把当前栈帧出栈,因此退出时可能执行的操作有:恢复上层方法的局部变量表和操作数栈,把返回值(如果有的话)压入调用者栈帧的操作数栈中,调整PC计数器的值以指向方法调用指令后面的一条指令等。

虚拟机栈Error

Java虚拟机栈有可能出现的error就是StackOverflowErrorOutOfMemoryError。当线程请求的栈深度大于Java虚拟机栈允许的深度时,就会抛出StackOverflowError错误。比如将一个方法反复递归,最终就会出现StackOverflowError。当Java虚拟机栈可以动态扩展时(大部分的 Java 虚拟机都可动态扩展,不过 Java 虚拟机规范中也允许固定长度的虚拟机栈),如果无法申请到足够的内存来扩展栈,就会抛出OutOfMemoryError错误。

本地方法栈

本地方法栈与虚拟机栈的功能类似,他们的区别在于虚拟机栈为执行Java代码方法服务,而本地方法栈是为Native方法服务。本地方法栈就是一个C的方法栈,本地方法栈的参数顺序、返回值和典型的C程序相同,本地方法一般来说可以(依赖 JVM 的实现)反过来调用 JVM 中的 Java 方法。这种native方法调用Java会发生在栈(一般是Java栈)上,线程将离开本地方法栈,并在 Java 栈上开辟一个新的栈帧。

与虚拟机栈一样,本地方法栈也会抛出StackOverflowErrorOutOfMemoryError

堆是Java虚拟机中最大的一块内存区域,它是有所有的线程共享。几乎所有的实例对象和数组都是在堆中存放。只要是通过new关键字创建对象或者直接声明数组,都会在堆中开辟内存空间来存放。因为在栈帧被创建后无法调整大小,栈帧中只能存放对象和数组在堆中的引用。方法或线程结束时对象和数组不会立即被移除销毁,它只能由垃圾回收器回收。

同样地,如果在堆中没有内存来完成实例分配,并且堆也无法扩展时,将会抛出OutOfMemoryError

方法区

方法区与堆一样,是各个线程共享的内存区域,它存储已经被虚拟机加载的类信息(包括字段信息、方法信息、方法代码等)、常量、静态变量、即时编译器编译后的代码等数据。

方法区中的内存一般不会被GC回收,GC也很难回收。方法区的内存回收主要是针对针对常量池的回收和对类的卸载。根据 Java 虚拟机规范的规定,当方法区无法满足内存分配需求时,将抛出OutOfMemoryError异常。

运行时常量池

运行时常量池是方法区的一部分,Class文件除了有关类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池,用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存放。运行时常量池可以理解为是类或接口的常量池的运行时表现形式。


参考:

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容