读书笔记
内存结构图:
程序计数器
用于存放下一条指令所在单元的地址的地方
虚拟机栈
常说的堆栈结构里的栈,就是这个。
定义:
虚拟机栈描述的是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都保存在栈里。通过两步字节码指令,将其保存至局部变量表里:
- bipush 100 将整数100推入操作栈栈顶。
- store_1 将栈顶1位置的数据出栈,然后保存至局部变量表。
可以在代码中通过 javap 字节码指令查看
堆
参考文章:
https://juejin.cn/post/6844904054561193992 https://www.cnblogs.com/ganchuanpu/p/6217342.html
对象在堆内存里分为仨部分:
- 对象头
- 实例数据
- 对齐填充
对象头:
- Mark Word 哈希码、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等
- 类型指针 对象指向它的类型元数据的指针、如果是数组还保存数据数量。
实例数据:
实际用到的数据
对齐填充:
保证是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里的类对象。