JVM01-运行时数据区

Java运行时数据区

以下理论来自于oracle JVM8虚拟机规范

PC(Program counter 程序计数器)

Java 虚拟机可以同时支持多个执行线程 每个线程都有自己的 PC 程序计数器

  每个线程都在执行单一方法的代码,即该线程的当前方法。 如果该方法不是本地方法,则 pc 包含当前正在执行的 Java 虚拟机指令的地址。
  
  如果线程当前正在执行的方法是非本地的,则 Java 虚拟机的 pc 的值是未定义的
复制代码

线程在运行的时候就是不停在计数器中取PC值,在把PC的值++ 找到对应的位置 继续执行 直到这个线程执行完毕

Stack 栈

栈的创建时间是线程启动的时候 栈中的数据都是对应线程私有独享的

保存局部变量和部分结果,并在方法调用和返回中发挥作用

 1. 栈只有push 和 pop 操作 

 2. 栈是不需要内存是连续的 所以栈帧的数据有可能被分配在堆中的这一点被JVM虚拟机称之为堆栈

 3. 堆栈jvm是允许用户自己设定大小的,如果大小是固定的则会根据实际数据计算需要动态扩容和收缩的 可以通过-Xss设置
 
 4. 如果线程使用超过设定的栈的大小 那么虚拟机是会StackOverflowError的错误的
    比如写一个死循环的递归 就会抛出栈溢出的错
 
 5. 如果虚拟机的堆栈可以动态扩容,但是扩容的大小超过了系统可用内存的大小,那么堆栈是会抛出OutOfMemoryError
复制代码

栈中的内存不是连续的 ,但是栈帧是有可能会被分配在堆上面的 jvm虚拟机将这种称之为堆栈, 栈中的内存数据如果超出了栈的大小或者系统内存大小是会抛出栈溢出的错误,如果是堆栈超出了大小则是内存溢出的错误

栈帧Frame

栈帧中存放

  1. Local Variable Table 局部变量
 public static void main(String[] args) {
      new TestObject().testRun();

  }
复制代码

image.png

image.png
2. Operand Stack 操作栈
JVM指令集查看

i=i++的指令

JMM (3).jpg

int i =0;
i=i++;
以上语句对应下面指令
 iconst_0 //将i的值压栈
 istore_1 //存储到局部变量表
 iload_1  //将局部变量索引为1的拿出来 压栈
 iinc 1 by 1 //变量索引为1的直接在变量表中进行运算 这个时候并不影响操作栈中的值
 istore_1 //从操作数栈中弹出栈顶 并将局部变量索引为1的值设置为value。因为这个时候操作栈栈顶的值还是0 所以最终i的值依旧为0
 return
 
 所以这个指令的结果是 i=0 -> i=1 -> i=0 最终i=0 
 
复制代码

i=++i的指令

JMM (4).jpg

int i =0;
i = ++i;
以上代码对应的指令
 iconst_0 
 
 istore_1
 
 iinc 1 by 1
 将局部变量表的值 直接在局部变量表的slot上进行运算 不影响操作栈中的值
 
 iload_1 
 从局部变量加载index为1的变量 压入操作栈 这个时候操作栈就为1
 
 istore_1 
 // 从操作数栈中弹出栈顶 并将局部变量索引为1的值设置为value
 
 //所以这个指令的结果是 i=0 -> i=1 -> i=1 最终i=1
复制代码
  1. Dynamic Linking
    指向对应的符号链接方法区中常量池 对象类型 等

  2. return adress
    返回值是下一个栈帧的地址 比如调用某一个方法get() get对应的返回值 返回对应的位置 继续执行

Heap 堆

堆的创建时间是JVM虚拟机启动的时候

堆也被称之为共享堆

  1、堆中的数据是JAVA运行时数据区 分配数组和对象的内存 堆中的数据由垃圾回收器回收
  
  2、堆的大小可以是固定的也可以是自动收缩的 可以通过参数配置 -Xmx
  
  3、堆中的内存也是非连续内存
  
  4、如果计算需要的堆多于自动存储管理系统所能提供的堆则抛出 OutOfMemoryError
复制代码

Method Area 方法区

方法区是一个逻辑概念,实际实现方式

jdk 1.8是在MetaSpace就是元空间
jdk 1.8之前是在PermGenSpace 永久代
复制代码

方法区的创建时间是JVM虚拟机启动的时候

方法区是所有线程共享的一个内存区域

  方法区存储的是已经编译代码,类似于操作系统进程中的“文本”段
  例如Class文件中的 运行常量池 字段 和 方法数据 以及构造函数的代码 包括类在初始化的时候需要使用到的特殊方法<init> 
  
  方法区在逻辑上是堆的一部分,但是实现可以选择不回收或者压缩它
  
  方法区的内存也是不连续的,同样也可以指定方法区的大小,同样可以收缩扩容 如果方法区的内存溢出同样也会抛出 OutOfMemoryError
  
复制代码

Run-Time Constant Pool 运行时常量池

运行时常量池是类文件中常量池表的每个类或每个接口的运行时表示

   包含多种常量,从编译时已知的数字文字到必须在运行时解析的方法和字段引用
   
   每一个运行时常量池都是从方法区分配的
   
   类或接口的运行时常量池是在 Java 虚拟机创建类或接口时就是ClassLoader加载类的时候
   
   如果运行时常量池的构造需要比 Java 虚拟机的方法区中可用的内存更多的内存,则 Java 虚拟机将抛出 OutOfMemoryError
   
复制代码

Direct Memory 直接内存

一般用于写文件 或者 读写网络IO流的时候使用 直接从用户态访问内核态

比如网卡接收到数据 数据被放到os中内核空间的缓冲区的内容 这边可以采用Direct Memory直接访问或者写入 也叫zero copy(零拷贝) netty采用的就是这一块内存的使用

当直接内存溢出的时候会抛出 BufferOverflowException

Native Method Stacks 本地方法栈

本地方法栈一般被称之为C的堆栈 或者叫Java 虚拟机的指令集

  在创建线程时 如果需要调用到本地方法 则会创建一个对应线程的本地方法堆栈
  
  线程中的计算需要比允许的更大的本机方法堆栈 则Java 虚拟机抛出 StackOverflowError
  
  如果可以动态扩容本机方法堆栈并尝试进行本机方法堆栈扩容时,但可用内存不足,或者如果可用内存不足为新线程创建初始本机方法堆栈,则抛出 OutOfMemoryError
  
复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享