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
栈帧中存放
- Local Variable Table 局部变量
public static void main(String[] args) {
new TestObject().testRun();
}
复制代码
2. Operand Stack 操作栈
JVM指令集查看
i=i++的指令
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的指令
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
复制代码
-
Dynamic Linking
指向对应的符号链接方法区中常量池 对象类型 等 -
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