JVM内存结构 - 图1

PC寄存器

作用:记住下一条要执行的二进制字节码指令地址(经过解释器翻译成机器码交由CPU运行)
特点:线程私有的;不会存在内存溢出。

  • 每个线程拥有一个PC寄存器
  • 在线程创建时 创建
  • 指向下一条指令的地址
  • 执行本地方法时,PC的值为undefined

    方法区

  • 保存装载的类信息

    • 类型的常量池
    • 字段,方法信息
    • 方法字节码
  • 通常和永久区(Perm)关联在一起

    通过new关键字,创建对象都会使用堆内存;
    特点:

  • 它是线程共享的,堆中对象都需要考虑线程安全问题

  • 有垃圾回收机制
  • 和程序开发密切相关
  • 应用系统对象都保存在Java堆中
  • 所有线程共享Java堆
  • 对分代GC来说,堆也是分代的
  • GC的主要工作区间

image.png

作用:限定仅在表头进行插入和删除操作的线性表。即压栈(入栈)和弹栈(出栈)都是对栈顶元素进行操作的。所以栈是后进先出的。

  • 线程私有
  • 栈由一系列帧组成(因此Java栈也叫做帧栈)
  • 帧保存一个方法的局部变量、操作数栈、常量池指针
  • 每一次方法调用创建一个帧,并压栈

    局部变量表

    栈帧中,由一个局部变量表存储数据。局部变量表中存储了基本数据类型(boolean、byte、char、short、int、float、long、double)的局部变量(包括参数)、和对象的引用(String、数组、对象等),但是不存储对象的内容。局部变量表所需的内存空间在编译期间完成分配,在方法运行期间不会改变局部变量表的大小。
    局部变量的容量以变量槽(Variable Slot)为最小单位,每个变量槽最大存储32位的数据类型。对于64位的数据类型(long、double),JVM 会为其分配两个连续的变量槽来存储。以下简称 Slot 。
    JVM 通过索引定位的方式使用局部变量表,索引的范围从0开始至局部变量表中最大的 Slot 数量。普通方法与 static 方法在第 0 个槽位的存储有所不同。非 static 方法的第 0 个槽位存储方法所属对象实例的引用。 ```java public class StackDemo {

    public static int runStatic(int i,long l,float f,Object o ,byte b){

    1. return 0;

    }

    public int runInstance(char c,short s,boolean b){

    1. return 0;

    }

}

  1. 字节码编译<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/22478710/1653325360129-5b35af87-e792-4fd5-981b-f741d94e7393.png#clientId=uc8b5b345-f5ad-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=777&id=ucb3c0fc5&margin=%5Bobject%20Object%5D&name=image.png&originHeight=1166&originWidth=957&originalType=binary&ratio=1&rotation=0&showTitle=false&size=106050&status=done&style=none&taskId=u8fe7f1dc-46dd-4dab-bff7-0bd2a41b5b0&title=&width=638)<br />可以看到每个方法内的局部变量。
  2. <a name="zCkhb"></a>
  3. ## 函数调用组成帧栈
  4. 接着,我们使用递归来看
  5. ```java
  6. public static int runStatic(int i, long l, float f, Object o, byte b) {
  7. return runStatic(i, l, f, o, b);
  8. }

image.png