对象的内存布局简介
在 Hotspot 虚拟机里,对象在堆内存中存在布局可划分为三个部分: 对象头(Header), 实例数据(Instance Data) 和对齐填充(Padding 保证8字节位数)
对象头
- 对象标记 MarkWord, 在64位操作系统中, Mark Word 占8个字节, 类型占 8个字节,一共 16个字节
- GC 年龄采用 4 位 bit 存储, 最大为 15, MaxTenuringThreshold = 15
Mark Word 标记:
存储内容 | 标志位 | 状态 |
对象哈希码、对象分代年龄 | 01 | 未锁定 |
指向锁记录的指针 | 00 | 轻量级锁定 |
指向重量级锁的指针 | 10 | 膨胀(重量级锁) |
空、不需要记录信息 | 11 | GC 标记 |
偏向线程ID、偏向时间戳、对象分代年龄 | 01 | 可偏向 |
Mark Word的存储结构:
锁状态 | 25bit | 31bit | 1bit | 4bit | 1bit | 2bit |
cms_free | 分代年龄 | 偏向锁 | 锁标志位 | |||
无锁 | unused | hashCode | 0 | 01 | ||
偏向锁 | ThreadID(54 bit) Epoch(2bit) | 1 | 01 | |||
轻量级锁 | 指向栈中的锁的记录的指针 | 00 | ||||
重量级锁 | 指向重量级锁的指针 | 10 | ||||
GC 标志 | 空 | 11 |
类元数据(类型指针):
- 对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例
实例数据
- 存放类的属性(Field)数据信息,包括父类的属性信息
对齐填充
- 虚拟机要求对象起始地址必须是8的字节的整数倍。填充数据不是必须存在的,仅仅是为了字节对齐这部分内存按 8 字节补充对齐
实例代码:
import org.openjdk.jol.info.ClassLayout;
import org.openjdk.jol.vm.VM;
public class JOLDemo {
public static void main(String[] args){
// // VM 的细节详细情况
// System.out.println(VM.current().details());
// // 所有的对象分配的字节都是 8 的整数倍
// System.out.println(VM.current().objectAlignment());
// OFFSET 偏移量,也就是这个字段位置所占用的 byte 数
// SIZE 后面类型的字节大小
// TYPE 是 Class 中定义的类型
// DESCRIPTION 类型的描述
// TYPE 在内存中的值
// 前两行是 MarkWord 8 字节, 第三行是 类型指针 4 字节
// 对象头情景1
Object o = new Object();
System.out.println(ClassLayout.parseInstance(o).toPrintable());
// 对象头 情景2
Customer c1 = new Customer();
System.out.println(ClassLayout.parseInstance(c1).toPrintable());
}
}
class Customer{
int id;
boolean flag = false;
}
压缩指针:
- java -XX: +PrintCommandLineFlags-version 展示默认适用情况
- 默认开启压缩指针(-XX:+UseCompressedClassPointers), 12 + 4(对齐填充) 一个对象 16个字节
- 关闭压缩指针(-XX:-UseCompressedClassPointers),Mark Word 占8个字节, 类型占 8个字节,一共 16个字节
底层代码
oop.hpp:
// 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; }
void set_mark(volatile markOop m) { _mark = m; }
void release_set_mark(markOop m);
markOop cas_set_mark(markOop new_mark, markOop old_mark);
// Used only to re-initialize the mark word (e.g., of promoted
// objects during a GC) -- requires a valid klass pointer
void init_mark();
Klass* klass() const;
Klass* klass_or_null() const volatile;
Klass** klass_addr();
narrowKlass* compressed_klass_addr();
void set_klass(Klass* k);
// For klass field compression
int klass_gap() const;
void set_klass_gap(int z);
// For when the klass pointer is being used as a linked list "next" field.
void set_klass_to_list_ptr(oop k);
oop list_ptr_from_klass();
// size of object header, aligned to platform wordSize
static int header_size() { return sizeof(oopDesc)/HeapWordSize; }
// Returns whether this is an instance of k or an instance of a subclass of k
bool is_a(Klass* k) const;
......
}
markOop.hpp:
#ifndef SHARE_VM_OOPS_MARKOOP_HPP
#define SHARE_VM_OOPS_MARKOOP_HPP
#include "oops/oop.hpp"
// The markOop describes the header of an object.
//
// Note that the mark is not a real oop but just a word.
// It is placed in the oop hierarchy for historical reasons.
//
// Bit-format of an object header (most significant first, big endian layout below):
//
// 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)