内存原理
参数拷贝传值
基本类型是拷贝传值,内部拷贝间交换不会影响外部值
1 | void swapNoChange(int v1,int v2){ |
引用也是传值,传的是引用的地址的拷贝,内部拷贝间交换同样不会影响外部值
1 | void swapNoChange(Person p1,Person p2){ |
修改对象内部时,外部对象真的改变了,因为指向的内存是同一位置,在函数内修改了内存,影响了外部
1 | void set(Person p){ |
对象底层结构
JVM底层C++实现oop-klass模型,即对象实例和元数据分离存储的二分模型
- oop: Ordinary Object Pointer 对象指针,描述实例
- klass: 元数据及方法信息,描述类
JVM运行时加载一个类时,会在JVM内部创建一个instanceKlass对象,表示这个类的运行时元数据
创建一个这个Class的实例时,会在JVM内部创建一个instanceOop来表示这个Java对象
instanceKlass对象放在了方法区/元空间,instanceOop放在了堆,instanceOop的引用放在了JVM栈
新对象创建过程
- 检查加载
- 分配内存
- 成员默认初始化
- 对象头填充
对象空间大小
Java的对象大小是平台无关的,不像C++。
基本类型大小
Java规定了每种类型的大小:
- 8字节 double/long
- 4字节 int/float
- 2字节 short/char
- 1字节 byte
除了布尔
类型,在每种类型下都有一个SIZE
常量表示占用比特位数。布尔类型在规范中没明确定义,取决于虚拟机实现,可能1字节也可能采取整型4字节。布尔型可采用int,布尔数组采用byte数组
1 | System.out.println(Double.SIZE); // 64 |
引用大小
引用可以看成地址,跟寻址空间有关
32位JVM就是4字节,64位就是8字节
对象大小
没规定具体的内存结构,不同的JVM结构有所不同
https://www.pianshen.com/article/2382167638/
2~3个字宽存储对象头,字宽由32/64位决定
- Mark Word:存储内容根据不同的状态而不同并非同时全部存在,比如无锁时包含哈希码,偏向锁时包含线程id,轻量级锁时包含锁记录指针,重量级锁时包含monitor指针等,GC标记
- Class Meta Address:类元信息指针
- Array Length: 数组专有
HotSpot为例
- 对象头
- 对象标记(Mark Word),32位下4字节,64位下8字节
- 类元信息,类地址
- 实例信息
- 类成员,本类及所有可见父类成员
- 数组对象还有额外4字节储存长度信息
- 对象填充,以8字节为粒度对齐,这样不会跨缓存行
32位与64位小结
- 32位处理器一次处理32位的指令,64位则一次处理64位,在相同工作频率下64位更高效
- 32位寻址空间2的32次方最大4GB,64位则是上千万个TB,32位JVM堆大小理论上限4G,实际不能达到,不同操作系统不同,64位JVM堆理论大小非常大,通常只受限于物理机
- 无论是32位还是64位,Java的基本类型大小都是固定的,比如int就是32位
- 并非64位就比32位性能好,如果32位即可满足,移植到64位性能反而可能下降,因为64位提供了不必要的大空间
UseCompressedOops
在64位环境下,如果仍需要32位结构,可以开启压缩指针选项
纯32位环境下,寻址空间只有4G
开启压缩指针后,由于8字节对齐,隐含相当于末尾3个0,即全部地址物理上只32位,但是效果上可以35位,多出的3位使得寻址空间扩大8倍到32G
超过32G只能采用纯64位指针
实例
32位环境下的Integer对象,共16字节
- MarkWord 4字节
- Class类指针 4字节
- 类成员(int value) 4字节
- 对齐 4字节
32位环境下的Integer[3]数组对象
- MarkWord 4字节
- Class类指针 4字节
- 长度(int length) 4字节
- 数组元素3个引用 4字节*3
- 对齐 0字节
测试
利用Agent机制获取Instrumentation
1 | java -javaagent:test.jar -XX:-UseCompressedOops -jar test.jar |
利用Instrumentation的getObjectSize方法获取对象大小
1 | public class SizeOfObject { |
版权声明
This site by Linest is licensed under a Creative Commons BY-NC-ND 4.0 International License.
由Linest创作并维护的博客采用创作共用保留署名-非商业-禁止演绎4.0国际许可证。
本文永久链接:http://linest.github.io/2018/12/15/java-memory-structure/