JVM

《深入理解Java虚拟机JVM》 - 内存结构

Posted by boredream on April 1, 2020

读书笔记

内存结构图:
jvm1

程序计数器

用于存放下一条指令所在单元的地址的地方

虚拟机栈

常说的堆栈结构里的栈,就是这个。

定义:

虚拟机栈描述的是Java方法执行的内存模型:

  • 每个方法在执行的同时都会创建一个栈帧用于存储
  • 局部变量表、操作数栈、动态链接、方法出口等信息。
  • 每个方法从调用到执行完成的过程,就对应一个栈帧从入栈到出栈的过程。

误区:

以前看资料说栈的作用是持有引用/句柄

1
2
int i = 1; 
Object o = new Object()

i 和 o 都在栈中,new Object()对象是在堆中。(字符串或静态变量指向的对象在方法区里的运行时常量池里)
但不完全对,这只是栈帧里局部变量表的作用,此外还有操作数栈等其他作用。
且如果是成员变量的话,则引用也是在堆里,而非栈里的(所以子线程也可以访问,因为堆是所有线程共享数据的)

栈帧:

  • 局部变量表
    一组变量值存储空间,用于存放方法参数和方法内局部变量, 以变量槽Variable Slot为最小单位,每个槽32位长度空间,对于long和double会占用俩连续槽

  • 操作数栈
    栈中栈~执行具体命令的栈,比如保存、逻辑运算、赋值等操作

  • 动态链接
    每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态连接

  • 方法出口
    正常return或异常退出情况,都要返回到方法调用的地方继续运行代码,所以要持有一个调用处的引用??

实例:

局部变量int a=100。
其中a和1都保存在栈里。通过两步字节码指令,将其保存至局部变量表里:

  1. bipush 100 将整数100推入操作栈栈顶。
  2. store_1 将栈顶1位置的数据出栈,然后保存至局部变量表。

可以在代码中通过 javap 字节码指令查看

参考文章:
https://juejin.cn/post/6844904054561193992 https://www.cnblogs.com/ganchuanpu/p/6217342.html

对象在堆内存里分为仨部分:

  • 对象头
  • 实例数据
  • 对齐填充

对象头:

  1. Mark Word 哈希码、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等
  2. 类型指针 对象指向它的类型元数据的指针、如果是数组还保存数据数量。

实例数据:

实际用到的数据

对齐填充:

保证是8字节/64bit的整数倍

方法区:

用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。
方法区是概念,具体实现有 JDK8 之前 HotSpot 虚拟机的 Permanent Generation 永久代,以及 JDK8 之后 HotSpot 的 Meta-space 元空间。

内存结构实战分析:

参考文章:
https://juejin.cn/post/6844904055421009928

1
Dog dog = new Dog();

dog在栈里,指针指向堆里Dog对象实例。
new的对象在堆里,这部分内存具体包括对象头(mark word + 类型指针)、实例数据、对其填充(可选)。
类型指针指向在方法区(hotpot 1.8方法区是在本地内存的元数据空间实现的)的类型信息 / 类元数据。
类型信息 / 类元数据是Klass对象,是c中类的对象,保存类名、限定符、常量池、方法字典等。
而 XXX.class 的类对象是在堆里的一个普通对象,是java里的类对象。 jvm2