5-运行时数据区

一.运行时数据区

一个class文件被classloader加载到内存,经过load/link/initialize三步之后,然后交给jvm的运行时引擎运行,运行的时候在内存里面是一个什么情况?

根据《Java虚拟机规范》的规定,Java虚拟机所管理的内存将会包括以下几个运行时数据区域。

程序计数器(Program Counter Register):是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。

java虚拟机栈(JVM stacks): 线程私有的,它的生命周期与线程相同。虚拟机栈描述的是Java方法执行的线程内存模型:每个方法被执行的时候,Java虚拟机都会同步创建一个栈帧(Stack Frame)用于存储局部变量表操作数栈动态连接方法出口等信息。

本地方法栈(Native Method Stacks):与虚拟机栈所发挥的作用是非常相似的,其区别只是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的本地 (Native) 方法服务。

Java堆(Java Heap):是虚拟机所管理的内存中最大的一块。Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,Java 世界里“几乎”所有的对象实例都在这里分配内存。

方法区(Method Area):是一种逻辑划分。与Java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类型信息(类名、访问修饰符、字段描述、方法描述等)、常量池(静态常量在编译器存入class文件常量池)、静态变量、即时编译器编译后的代码缓存等数据。

jdk1.8之前叫永久代(Perm Space)字符串常量位于Perm Space,FGC不会清理。

jdk1.8之后叫元数据区域(Meta Space )包含类型信息/运行时常量池(run-time constant pool)。字符串常量位于堆,会触发FGC。

直接内存(Direct Memory):jvm可以直接访问的内存(os管理的内存)。NIO,提高效率,实现zero copy。

二.分析java虚拟机栈(JVM stacks)

Frame(栈桢):用于存储局部变量表操作数栈动态连接方法出口等信息。每一个方法被调用直至执行完毕的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。

局部变量表(local variables):存放了编译期可知的各种Java虚拟机基本数据类型(boolean、byte、char、short、 int、 float、long、double)、对象引用。

操作数栈(operand stacks)

动态连接(dynamic linking):动态链接是一个将符号引用解析为直接引用的过程。java虚拟机执行字节码时,遇到一个操作码,操作码第一次使用一个指向另一类的符号引用,则虚拟机就必须解析这个符号引用。

方法出口(return address): a() -> b(),方法a调用了方法b, b方法的返回值放在什么地方

让我们从一个小demo分析一下jvm stacks是如何运作的。

public class Test1 {
    public static void main(String[] args) {
        int i = 1;
        i = i++;
//        i = ++i;
        System.out.println(i);
    }
}

输出:1

我们来分析一下字节码:

  1. icons_1 把1压入操作数栈(栈顶为1)
  2. istore_1 把1出栈存储到局部变量表索引为1的位置(i此时为1,栈为空)
  3. iload_1 把局部变量表索引为1的值压入操作数栈(栈顶为1)
  4. iinc 1 by 1 把局部变量表索引为1的值加1(i此时为2)
  5. istore_1 操作数栈出栈将值赋给局部变量表索引为1的位置(i此时为1,栈为空)

所以最终局部变量表索引为1位置(即i)的值为1。

再来看看这个小demo

public class Test1 {
    public static void main(String[] args) {
        int i = 1;
//        i = i++;
        i = ++i;
        System.out.println(i);
    }
}

输出:2

我们来分析一下字节码:

  1. icons_1 把1压入操作数栈
  2. istore_1 把1出栈存储到局部变量表索引为1的位置
  3. iinc 1 by 1 把局部变量表索引为1的值加1 (i此时为2)
  4. iload_1 把局部变量表索引为1的值压入操作数栈
  5. istore_1 操作数栈出栈将值赋给局部变量表索引为1的位置(i此时为2)

所以最终局部变量表索引为1位置(即i)的值为2。

posted @ 2020-08-05 21:18  张天赐的博客  阅读(119)  评论(0编辑  收藏  举报