关于java:Java虚拟机运行时数据区

21次阅读

共计 2287 个字符,预计需要花费 6 分钟才能阅读完成。

简图


线程公有区域:虚拟机栈、本地办法栈和程序计数器
线程共享区域:堆和办法区(元空间区)

线程公有区域

程序计数器

作用:读取程序计数器的值来选取下一条字节码指令,并实现分支、循环、跳转、异样解决、线程回复等

  • 程序计数器是一个很小的内容空间,能够看做是以后线程所执行的字节码的行号指示器。在 Java 虚拟机的概念模型中,字节码解释器工作是通过扭转这个计时器的值来选取下一条须要执行的字节码指令,他是程序控制流的指示器,分支、循环、跳转、异样解决、线程回复等根底性能都须要依赖这个计数器来实现
  • 每个线程都有一个独立的线程计数器,各条线程之间计时器互不影响,独立存储。生命周期与线程的生命周期保持一致
  • 如果线程正在执行的是一个 Java 办法,程序计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是本地办法(调用 C 类库执行)程序计数器为空(undefined)
  • 它是惟一一个在《Java 虚拟机标准》中没有规定任何 OutOfMemoryError 状况的区域

编译如下代码后应用 javap 反编译工具查看字节码文件Test.class

public class Test {public static void main(String[] args) {
        int a = 1;
        int b = 2;
        System.out.println(a + b);
    }
}
//    指令:javap -v Test.class

在反编译后果中找到 main() 办法

途图中红框内的数字为 指令地址,执行引擎通过读取指令地址实现办法的运行

虚拟机栈

作用:在 Java 程序运行期间,它保留办法的局部变量、局部后果,并参加办法的调用和返回

  • 每个办法执行的时候,Java 虚拟机都会同步创立一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动静链接、办法进口等信息,对应着一个栈帧在虚拟机栈中从入栈到出栈的过程
  • 属线程公有区域
  • 生命周期与线程生命周期雷同
  • 此区域规定了两类异样情况:如果线程申请的栈深度大于虚拟机的所容许的深度,将抛出 StackOverFlowError 异样;如果 Java 虚拟机栈容量能够动静扩大,当栈扩大时无奈申请到足够的内存会抛出 OutOfMemoryError 异样
  • 应用 -Xss 设置栈的大小;栈的大小间接决定了函数调用的最大可达深度
  • 栈的深度通常只有几百 K,然而它决定了函数调用深度;如果程序应用了很深的递归函数,而栈空间十分小,此时很有可能产生栈溢出(java.lang.StackOverflowError)
  • 对于多线程工作,应放大栈,反之
虚拟机栈的特点:
  1. 是一种疾速无效的调配存储形式,访问速度仅次于程序计数器
  2. JVM 间接对 Java 栈的操作只有两个:

    1. 办法的执行对应入栈
    2. 办法完结执行对应出栈
  3. 虚拟机栈不存在垃圾回收问题
调整栈的大小

通过在 Idea 中设置 -Xss 参数,察看代码的执行状况

  • 设置栈大小为 128k,当栈深度为 967 时抛出栈溢出异样

  • 设置栈大小为 256k,当栈深度为 2250 时抛出栈溢出异样

栈帧的内部结构
  • 局部变量表

    • 定义为一个数字数组,次要用于存储办法参数和定义在办法体内的局部变量,这些数据类型包含八种根本数据类型、对象援用,以及 returnAddress 类型
    • 局部变量表建设在线程的栈上,是线程公有数据,因而不存在数据安全问题
    • 局部变量表所需的容量大小是编译期确定下来的, 在运行期间不会扭转局部变量表的大小

  • 操作数栈

    在办法执行过程中,依据字节码指令,往栈中写入数据或提取数据,即入栈和出栈。应用 javap 指令可查看操作数栈的深度

  • 动静链接

每一个栈帧外部都蕴含一个指向运行时常量池中该帧所属办法的援用。蕴含这个援用的目标就是为了以后办法的代码可能实现 动静链接。动静链接的作用是将符号援用转换为调用办法的间接援用。

  • 办法返回地址

    寄存调用该办法的的 PC 寄存器的值;程序失常退出时返回该值,异样退出时不返回该值

  • 一些附加信息

本地办法栈

本地办法栈用于治理本地办法的调用(c 或 c ++),会抛出 StackOverFlowErrorOutOfMemoryError

线程共享区域

  • 一个 Java 过程对应一个 JVM 实例,一个 JVM 实例存在一个运行时数据区,多个线程共享堆和办法区
  • JVM 启动时堆被创立且空间大小以确定,是 JVM 治理的最大的一个内存区域

    • 堆的大小是可调节的
  • 堆能够处于物理不间断,但在逻辑上应该是视为间断的
  • 所有的线程共享堆,还能够划分出线程公有缓冲区(TLAB)
  • 大多数对象实例和数组都调配在堆上
  • 数组和对象可能永远不会存储在栈上,栈帧中保留援用,援用指向堆中的地位
  • 办法执行完结后,堆中的对象不会立刻移除,在垃圾收集的时候才会被移除
  • 此区域是 GC 的次要工作区域
细分堆内存
  • Java7 及之前

    • 年老代
    • 老年代
    • 永恒代
  • Java8 及之后

    • 年老代

      • Eden Space、Survivor0、Survivor1
    • 老年代:Old Gen
    • 元空间:Metaspace
设置堆空间的的大小

-Xms:memory start;用来设置堆(年老代 + 老年代)的 初始内存 大小

-Xmx:memory max;用来设置堆(年老代 + 老年代)的 最大内存 大小

-XX:+PrintGCDetails 输入 GC 信息

年老代和老年代

  • 应用 -XX:NewRatio 调整年老代和老年代的比例,例如:-XX:NewRatio=3,示意年老代占 1 份,老年代占 3 份;对应四分之一和四分之三
  • 默认状况下-XX:NewRatio=2
  • 年老代中的分配比例为 8:1:1,应用 -XX:-UseAdaptiveSizePolicy 敞开自适应内存调配策略
  • 应用 -Xn 设置年老代的空间

办法区

参考资料

  1. 【Java 虚拟机探索】5. 罕用 JVM 配置参数 - 栈的调配参数
  2. 深刻了解 Java 虚拟机 - 周志明(第二版)

正文完
 0