深入理解Java虚拟机(二) --- JVM内存管理

文章目录运行时的数据区域一.程序计数器二.Java虚拟机栈局部变量表:操作数栈三.本地方法栈四.Java堆五.方法区(非堆)运行时常量池直接内存运行时的数据区域一.程序计数器目的:作为当前线程所执行字节码的行号指示...

文章目录

  • 运行时的数据区域
  • 一.程序计数器
  • 二.Java虚拟机栈
    • 局部变量表:
    • 操作数栈
  • 三.本地方法栈
  • 四.Java堆
  • 五.方法区(非堆)
    • 运行时常量池
  • 直接内存


运行时的数据区域



一.程序计数器

目的:作为当前线程所执行字节码的行号指示器

原理:通过字节码解释器改变计数器的值来选取下一条字节码指令

特点
1.占用较小的内存空间

? 2.每条线程需要一个独立的程序计数器

? 3.Native方法不需要程序计数器,因为它不需要解释器

? 4.不同线程的该内存区域相互独立

方法类型 计数器状态
Java方法 记录正在执行字节码地址
Native方法

异常处理: 当没有内存区域可扩展的时候,抛出OutOfMemoryError



二.Java虚拟机栈

目的:描述Java执行时的内存模型

原理:当一个JAVA方法执行的时候,会在虚拟机栈上建立一个栈帧,用来保存 局部变量表操作数栈,动态键栈,方法出入的信息(eg:方法返回值)。

特点
1.广义上的栈就是指的是,虚拟机栈中的几部变量表

? 2.进入一个方法时,局部变量空间是确定的,不会再改变

? 3.不同线程的该内存区域相互独立

异常处理
1.若线程请求的栈深度 > 虚拟机允许的深度,抛出StackOverflowError

? 2.若动态虚拟机栈可以动态扩展(绝大多数),若扩展时无法申请足够的内存,抛出 OutOfMemoryError


局部变量表:

目的:使用 局部变量表 完成参数值到 参数变量列表 的传递过程

原理:是一个以字长为单位的数组,用于存放方法参数 和 方法内定义的局部变量索引

索引类型包括:基本变量类型,对象引用类型, returnAdress类型

1.reference类型(对象引用类型),作为对象引用指针,指明了对象在 JAVA堆 中的起始地址 和在 方法区 的对象类型数据
2. returnAdress类型,指向一条字节码指令的地址

  • 局部变量表中一个slot代表一个变量槽,长度和CPU架构的位长是相同的。

  • 64位长度的long和double类型占用2个局部变量表空间(slot),其他的均为1个单位大小。

  • 在第0位slot处存放该方法所属对象实例的引用(在Java堆中),程序中用this关键字进行访问

  • 可复用:超出作用域的slot变量将被GC回收进行覆写


操作数栈

目的:作为JVM的工作区,对数据进行操作。

原理:同样也是一个以字长为单位的数组,用于存放操作数的值。大多数指令都要从这里弹出数据,执行运算,然后把结果压回操作数栈。

解析:

void Method(){
    int a = 1;
    int b = 2;
    int result = a + b;
}

内部过程

1.在局部变量表分配三个slot,分别指向 a , b,result。其中 a,b有初值

2.将局部变量表中的a,b的值放入操作数栈。

3.在操作数栈中弹出a,b的值弹出进行运算,接着将结果压入操作数栈中。

4.从操作数栈弹出结果,放到局部变量表中result相应的位置



三.本地方法栈

目的:描述Native方法执行时的内存模型

原理: 形如Java虚拟机栈

特点: 不同线程的该内存区域相互独立

异常处理: 与Java虚拟机栈出现的异常是相同的

有些虚拟机会将本地方法栈与Java虚拟机栈合二为一(eg:HotSpot虚拟机)



四.Java堆

目的: 存放几乎所有对象的实例

原理: 将实例存放在一块 物理不连续,但是逻辑上连续的内存空间上。

特点:
1.是JVM管理的内存中最大的一块区域

? 2.所有线程共享一块内存区域

异常处理: 通常实例分配的时候,堆是可扩展的,但是当无法再扩展的时候,会抛出OutOfMemoryError



五.方法区(非堆)

目的: 作为Java堆的逻辑部分

原理: 存储已被虚拟机加载的 类信息,常量,静态变量,即时编译器编译后的代码 等数据

特点:
1.物理不连续

? 2.所有线程共享一块内存区域

? 3.可以选择不实现GC

异常处理: 虽然方法区是可以扩展的,但是当无法满足内存分配时,抛出OutOfMemoryError


运行时常量池

目的: 是方法区的一部分,用于存放编译期间生成的各种字面量与符号

原理: 在类加载后,将“常量”存放在该区域

特点: 动态性:在运行期间也能将新的常量放入池中

? eg:String类的intern方法(TODO )


JDK1.8后字符串常量池放到了堆中,不再是在方法区中,而是在Java堆中



直接内存

目的: 直接引用分配在Java堆外Native函数库的内存,避免了Java堆与Native堆来回复制操作

原理: 通过一个存储在Java堆中的DirectByteBuffer对象,作为这块内存的引用进行操作

特点: 显然直接内存分配不受Java堆大小限制,但受到本机物理的内存大小限制。

异常处理: 当占用总和超出物理内存大小限制的时候,抛出OutOfMemoryError

本文标题为:深入理解Java虚拟机(二) --- JVM内存管理