在JVM中对象在内存中的布局分为三块区域:
- 对象头
- 实例数据
- 对齐数据
对象头分类:
- instanceOopDesc:普通类型数据的对象头(重点)
- arrayOopDesc:数组类型数据的对象头
一,对象头
普通对象头包含的部分:
- Mark Work
- Klass pointer
当一个线程尝试访问synchronized修饰的代码时,它首先要先获得锁,这个锁就存在于对象头里
Hotspot采用instanceOopDesc(普通对象的对象头)和arrayOopDesc数组对象的对象头),instanceOopDesc定义在Hotspot源码的instanceOop.hpp文件中,arrayOopDesc定义在arrayOop.hpp
instanceOop.hpp的源码:
//它继承了oopDesc父类 class instanceOopDesc : public oopDesc { public: // aligned header size. static int header_size() { return sizeof(instanceOopDesc)/HeapWordSize; } // If compressed, the offset of the fields of the instance may not be aligned. static int base_offset_in_bytes() { // offset computation code breaks if UseCompressedClassPointers // only is true return (UseCompressedOops && UseCompressedClassPointers) ? klass_gap_offset_in_bytes() : sizeof(instanceOopDesc); } static bool contains_field_offset(int offset, int nonstatic_field_size) { int base_in_bytes = base_offset_in_bytes(); return (offset >= base_in_bytes && (offset-base_in_bytes) < nonstatic_field_size * heapOopSize); } };
oopDesc父类源码:这个oopDesc在oop.hpp文件中
class oopDesc { friend class VMStructs; private: volatile markOop _mark; union _metadata { Klass* _klass; narrowKlass _compressed_klass; } _metadata; // Fast access to barrier set. Must be initialized. static BarrierSet* _bs; public: markOop mark() const { return _mark; } markOop* mark_addr() const { return (markOop*) &_mark; } //省略其他代码
在普通实例对象中,oopDesc的定义包含两个成员,分别是_mark和_metadata:
- _mark表示对象标记,属于markOop类型,也就是Mark World,它记录了对象和锁有关的信息
- _metadata表示类元信息,类元信息存储的是对象指向它的类元数据(Klass)的首地址,其中Klass表示普通指针,_compressed_klass表示压缩类指针
对象头由两部分组成:
- Mark Word 存储自身运行时数据
- 指针
一,Mark Word
Mark Word用于存储对象自身的运行时数据:
- 哈希码(HashCode)
- GC分代年龄
- 锁状态标志
- 线程持有的锁
- 偏向线程ID
- 偏向时间戳
- 等等
占用内存大小与虚拟机位长一致,Mark Word对应的类型是markOop,源码位于markOop.hpp中
// 32 bits: // -------- // hash:25 ------------>| age:4 biased_lock:1 lock:2 (normal object) // JavaThread*:23 epoch:2 age:4 biased_lock:1 lock:2 (biased object) // size:32 ------------------------------------------>| (CMS free block) // PromotedObject*:29 ---------->| promo_bits:3 ----->| (CMS promoted object) // // 64 bits: // -------- // unused:25 hash:31 -->| unused:1 age:4 biased_lock:1 lock:2 (normal object) // JavaThread*:54 epoch:2 unused:1 age:4 biased_lock:1 lock:2 (biased object) // PromotedObject*:61 --------------------->| promo_bits:3 ----->| (CMS promoted object) // size:64 ----------------------------------------------------->| (CMS free block) // // unused:25 hash:31 -->| cms_free:1 age:4 biased_lock:1 lock:2 (COOPs && normal object) // JavaThread*:54 epoch:2 cms_free:1 age:4 biased_lock:1 lock:2 (COOPs && biased object) // narrowOop:32 unused:24 cms_free:1 unused:4 promo_bits:3 ----->| (COOPs && CMS promoted object) // unused:21 size:35 -->| cms_free:1 unused:7 ------------------>| (COOPs && CMS free block)
上面是JVM源码中对Mark Word内容的注释,它分为32位和64位
在64位虚拟机下,Mark Word是64bit大小的,其存储结构如下:
二,Klass Pointer
这一部分用于存储对象的类型指针,该指针指向它的类元数据,JVM通过这个指针确定对象是哪个类的实例。该指针的位长度为JVM的一个字大小,即32位的/VM为32位,64位的JV为64位
如果应用的对象过多,使用64位的指针将浪费大量内存,统计而言,64位的JVM将会比32位的JVM多耗费50%的内存、为了节约内存可以使用选项-XX:+UseCompressedOops开启指针压缩,其中,oop即ordinary objectpointer普通对象指针。开启该选项后,下列指针将压缩至32位:
1.每个Class的属性指针(即静态变量)
2.每个对象的属性指针(即对象变量)
3.普通对象数组的每个元素指针
当然,也不是所有的指针都会压缩,一些特殊类型的指针JVM不会优化,比如指向PermGen的Class对象指针(JDK8中指向元空间的Class对象指针)、本地变量、堆栈元素、入参、返回值和NULL指针等
对象头 = Mark Word + 类型指针(未开启指针压缩的情况下)
在32位系统中,Mark Word = 4bytes ,类型指针 = 4byte
在32位系统中,Mark Word = 8bytes ,类型指针 = 8byte
二,实例数据和对齐填充
实例数据:就是类中定义的成员变量
对齐填充: 对齐填充并不是必然存在的,也没有什么特别的意义,他仅仅起着占位符的作用,由于HotSpot VM的自动内存管理系统要求对象起始地址必须是8字节的整数倍,换句话说,就是对象的大小必须是8字节的整数倍。而对象头正好是8字节的倍数,因此,当对象实例数据部分没有对齐时,就需要通过对齐填充来补全
三,代码查看java对象布局
OpenJdk提供了专门看对象布局的依赖:
<dependency> <groupId>org.openjdk.jol</groupId> <artifactId>jol-core</artifactId> <version>0.9</version> </dependency>
注:这个依赖超过0.9版本,打印的对象bin不是二进制线程而是16近制
例:查看一个对象的布局
① 实例对象
public class lockobj { int a; }
② 打印实例对象布局
public class dome1 { public static void main(String[] args) { lockobj lockobj = new lockobj(); //解析lockobj对象并返回字符串 String s = ClassLayout.parseInstance(lockobj).toPrintable(); //打印 System.out.println(s); } }
③ 结果(默认开启了指针压缩)
④ 关闭对象指针压缩(-XX:-UseCompressedOops)
例:在无锁的情况下,有31个bit表示hashcode
①
public class dome1 { public static void main(String[] args) { lockobj lockobj = new lockobj(); lockobj.hashCode(); System.out.println(lockobj.hashCode()); System.out.println(Integer.toHexString(lockobj.hashCode())); //解析lockobj对象并返回字符串 String s = ClassLayout.parseInstance(lockobj).toPrintable(); //打印 System.out.println(s); } }
② 结果
Comments | NOTHING
Warning: Undefined variable $return_smiles in /www/wwwroot/wql_luoqin_ltd/wp-content/themes/Sakura/functions.php on line 1109