2.5运行时数据区

本贴最后更新于 3351 天前,其中的信息可能已经水流花落

    Java虚拟机定义了若干种程序运行期间会使用到的运行数据区,其中一些会随着虚拟机启动而创建,随着虚拟机退出而销毁。另外一些则是与线程一一对应的,这些与线程对应的数据区域随着线程的开始和结束而创建和销毁。

2.5.1 PC寄存器

    Java虚拟机可以支持多条线程同时执行(每一个JVM线程都有自己的PC(Program Counter,程序计数器)寄存器。在任意时刻,每一个JVM线程只会执行一个方法的代码,这个正在被线程执行的方法称为该线程的当前方法(Current Method))。如果这个方法不是native的,那么PC寄存器就保存JVM正在执行的字节码指令的地址,如果该方法是native的,那PC寄存器的值是undefined。PC寄存器的容量至少应当能保存一个returnAddress类型的数据或者一个与平台相关的本地指针的值。

2.5.2 JVM栈

    每一个JVM线程都有一个自己私有的JVM栈(Java Virtual Machine Stack),这个栈与线程同时创建,用于存储栈帧。JVM栈的作用与传统语言(例如C语言)中的栈非常相似,就是用于存储局部变量与一些过程结果的地方。另外,它在方法调用和返回中也扮演了重要的角色。因为除了栈帧的出栈和入栈之外,JVM栈不会再受其他因素的影响,所以栈帧可以在堆中分配¹,JVM栈所使用的内存不需要保证是连续的

    JVM规范允许JVM栈被实现成固定大小或者是根据计算动态扩展和收缩的。如果采用固定大小JVM栈设计,那每一个线程的JVM栈容量应当实在线程创建的时候独立选定的。JVM实现应当提供给程序员或者最终用户调节VM栈初始容量的手段,对于可以动态扩展和收缩JVM栈来说,应当提供调节其最大、最小容量的手段。

    JVM栈可能发生的异常情况如下:

  • 如果线程请求分配的栈容量超过JVM栈允许的最大容量时,JVM将会抛出一个StackOverflowError异常。
  • 如果JVM栈可以动态扩展,并且扩展的动作已经尝试过,但是目前无法申请到足够的内存去完成扩展,或者在建立新的线程是没有足够的内存去创建对应的JVM栈,那JVM将会抛出一个OutOfMemoryError异常。

2.5.3  Java堆

    在JVM中,堆(Heap)是可以提供各个线程共享的运行时内存区域,也是供所有类实例和数组对象分配内存的区域

    Java堆在JVM启动的时候就被创建,它储存了被自动内存管理系统(Automatic Storage Management System,也即是常说的“Garbage Collector(垃圾收集器)”)所管理的各种对象,这些受管理的对象无需,也无法显示的被销毁。本规范中所描述的JVM并未假设采用什么具体的技术去实现自动内存管理系统。VM实现者可以根据系统的实际需要来选择自动内存管理技术。Java堆得容量可以是固定大小的,也可以随着程序执行的需求动态扩展,并在不需要过多空间是自动收缩。Java堆所使用的内存不需要保证是连续的。

    JVM实现应当提供给程序员或者最终用户调节Java堆初始容量的手段,对于可以动态扩展和收缩的Java堆来说,则应当提供调节其最大、最小容量的手段。

    Java堆可能发生如下异常情况:

  • 如果实际所需要的堆超过了自动内存管理系统能提供的最大容量,那JVM将会抛出一个OutOfMemoryError异常。

2.5.4  方法区

    在JVM中,方法区(Method Area,很多人倾向于把方法区成为“永久代(Perm Generation))是可供各个线程共享的运行时内存区域。方法区与传统语言中的编译代码存储区(Storage Area Of Compiled Code)或者操作系统进程的正文段(Text Segment)的作用非常类似,它储存了每一个类的结构信息,例如运行时常量池(Runntime Constant Pool)、字段和方法数据、构造函数和普通方法的字节码内容、还包括一些在类、实例、接口初始化时用到的特殊方法。

    方法区在JVM启动的时候被创建,虽然方法区是堆得逻辑组成部分,但是简单的VM实现可以选择在这个区域不实现垃圾收集。这个版本的JVM规范也不限定实现方法区的内存位置和编译代码的管理策略。方法区的容量可以固定大小的,也可以随着程序执行的需要动态扩展,并在不需要过多空间时自动收缩。方法区在实际内存中可以使不连续的。

    JVM实现应当提供给程序员或者最终用户调节方法区初始容量的手段,对于可以动态扩展和收缩方法区来说,,则应当提供调节其最大、最小容量的手段。

    方法区可能发生如下异常情况:

  • 如果方法区内存的内存空间不能满足内存分配请求,那JVM将抛出一个OutOfMemoryError异常。

2.5.5  运行时常量池

    运行时常量池(Runtime Constant Pool)是每个类或者接口的常量池(Constant_Pool)的运行时表示形式,它包括了若干种不同的常量:从编译期可知的数值字面量到必须运行期解析后才能获得的方法或字段引用。运行时常量池扮演了类似传统语言中符号表的角色,不过它储存数据范围比通常意义上的符号表要更为广泛。

    每一个运行时常量池都分配在JVM的方法区中,在类和接口被加载到JVM后,对应的运行时常量池就被创建出来。

    在创建类和接口的运行时常量池时,可能会发生如下异常情况:

  • 当创建类或接口的时候,如果构造运行时常量池所需要的内存空间超过方法区所能提供的最大值,那JVM将会抛出一个OutOfMemoryError异常。

2.5.6  本地方法栈

    JVM实现可能会使用到传统的栈(通常称之为“C Stacks”)来支持native方法(指使用Java以外的其他语言编写的方法)的执行,这个栈就是本地方法栈(Native Method Stack)。当JVM使用其他语言(例如C语言)来实现指令集解释器时,也会使用到本地方法栈。如果JVM不支持native方法,并且自己也不依赖传统栈的话,可以无需支持本方法栈,如果支持本方法栈 ,那这个栈一般会在线程创建的时候按线程分配。

    JVM规范允许本地方法栈被实现成固定大小的或者是根据计算动态扩展和收缩的。如果采用笃定大小的本方法栈,那每一个线程的本地方法栈容量应当在栈创建的时候独立的选定。一般情况下,JVM实现应当提供给程序员或者最终用户调节VM栈初始容量的手段,对于长度可动态变化的本地方法栈来说,则应当提供调节其最大、最小容量的手段。

    本地方法栈可能发生如下异常情况:

  • 如果线程请求分配的站容量超过本地方法栈允许的最大容量时,JVM将会抛出一个StackOverrflowError异常。
  • 如果本地方法栈可以动态扩展,并且扩展的动作已经尝试过,但是母线无法申请到足够的内存去完成扩展,或者在建立新的线程是没有足够的内存去创建对应的本地方法栈,那JVM将会抛出一个OutOfMemoryError异常。

注:

    1.注意避免混淆Stack、Heap和Java Stack、Java Heap的概念,JVM的实现本身是由其他语言编写的应用程序,在Java语言程序的角度看分配在Java Stack中的数据,而在实现虚拟机的程度角度上看则可以是分配在Heap之中。

总结:

  1. PC寄存器,记录一个非native方法的正在执行的字节码指令地址(线程私有)
  2. JVM栈,存储栈帧。栈帧存储局部变量和过程结果。JVM栈在方法调用和返回中也起作用。栈帧可以在堆中分配(线程私有)
  3. Java堆,各个线程共享的运行时内存区域。也是供所有类实例和数组对象分配的区域。(线程共享)
  4. 方法区,存储每一个类的结构信息。例如:运行时常量池、字段、方法数据、普通方法的字节码内容等。(线程共享)
  5. 运行时常量池,在方法区中。是每一个类或者接口的常量池的运行时表示形式,包括若干种不同常量。
  6. 本地方法栈,当JVM使用其他语言来实现指令集解释器是,会用到本地方法栈。

最后附上一张图:

Java运行时数据区

----END----

  • 第2章Java虚拟机结构
    1 引用 • 2 回帖
  • Java虚拟机规范
    1 引用 • 2 回帖
  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3187 引用 • 8213 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...
  • someone

    第一进来 留个言 博主网站内容很丰富,学习了。欢迎去我那看看给点意见![em02]

  • someone

    恩恩,好的。[em01]