Java对象模型
Contents
对象在内存中的存储布局
对象存储在堆中,对象的引用在栈中,对象在内存中的存储布局如下:
- 对象头
- mark word
- GC分代年龄
- 锁状态标记
- 哈希码
- 类型指针
- length(只有数组对象包含)
- mark word
- 实例数据
- 对齐填充
对象的创建过程
- 类加载
- 内存分配
- 初始化零值
- 实际的初始化
- 返回引用
内存分配方式
- 指针碰撞 : 用于Serial和ParNew等不会产生内存碎片的垃圾收集器
- 为了解决多线程并发问题,引入TLAB。
- TLAB:预先为每个线程分配一块较大的空间,这样每个线程直接在自己的TLAB中进行内存分配,就不用考虑多线程的问题了!
- 可以用CAS为TLAB分配内存!
- 空闲列表: CMS垃圾收集器就是采用的这种方法
对象定位方式
- 句柄方式
- 在堆中划分一块区域作为句柄池,句柄中包含了类数据地址和实例数据地址
- 好处是对象位置发生改变时,只需要改变句柄就好,而不需要改变栈中的引用
- 坏处是访问麻烦
- 直接引用:HotSpot采用的时直接引用的方式。
- 引用直接指向对象的实例数据地址(实例对象的对象头总包含类数据地址)
- 好处是访问简单
- 坏处是对象位置改变时,需要改变栈中的引用。
对象空间分配过程
- 尝试栈上分配
- 逃逸分析:对象的作用范围只局限在方法内部
- 大对象可直接分配到老年代
- 尝试TLAB分配
- 伊甸分配
指针压缩
- 在64位平台的HotSpot中使用64位(8字节)指针,会占用更多内存,而且使用较大指针在主内存和缓存之间移动数据会占用较大宽带,GC也会承受更大压力
- 为了减小64位平台下的内存消耗,启用指针压缩功能
- 堆内存小于4G时,不需要启动指针压缩,jvm会直接除去高32位地址,使用低虚拟地址空间
- 堆内存大于32G时,压缩指针会失效,会强制使用64位来对java对象寻址
- 指针压缩原理:编码、解码
- 哪些信息会被压缩
- 对象的全局静态变量
- 对象头信息:64位平台下,原生对象头大小位16字节(markword 8字节,类型指针8字节),压缩后为12字节(markword 8字节,classpointer 4字节)
- 对象的引用类型,由8字节压缩至4字节
- 对象数组类型,由24字节压缩到16字节
- 哪些信息不会被压缩
- 指向非Heap对象的指针
- 局部变量、传参、返回值、Null指针
Object o = new Object()在内存中占用多少字节
- 64位平台下,采用压缩指针:16字节:8(mark word) + 4(class pointer)+4(对齐)
- 64位平台下,不采用压缩指针:16字节:8(mark word)+8(class pointer)
- o也占4个字节,所以满分答案是20字节。
HotSpot为什么不使用C++对象来代表java对象?
- 为了避免在每个对象中都含有一个虚函数指针。
- oop中不含任何虚函数,虚函数都在Klass对象中
- 在C++中,非虚函数在编译时就已经确定调用目标。
- 为了实现多态,也就是通过基类的指针调用子类的方法,才引入了虚函数
- 在C++中,只有虚函数才能被重写!java中的每个函数都是虚函数。
Class对象位于堆,还是方法区?
- 位于方法区。??应该位于堆中吧?
- 但是Class对象还会在堆区生成一个对象用于反射!
Author 段新朋
LastMod 2020-07-18