|
| 1 | +# 对象的创建 |
| 2 | + |
| 3 | +### 对象的创建 |
| 4 | + |
| 5 | +语言层面,创建对象(克隆、反序列化)通常仅仅是一个new关键字而已,但JVM遇到一条 new 指令时,首先 |
| 6 | + |
| 7 | + |
| 8 | + |
| 9 | +### 对象的内存布局 |
| 10 | + |
| 11 | +在 HotSpot 虚拟机中,对象在内存中存储的布局可以分为 3 块区域:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)。 |
| 12 | + |
| 13 | + |
| 14 | + |
| 15 | +#### 对象头 |
| 16 | + |
| 17 | +- **Mark Word**:用于存储对象自身的运行时数据,存储了对象的hashCode、GC信息、锁信息三部分。如哈希码、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等,这部分数据的长度在32位和64位的虚拟机(未开启压缩指针)中分别为 32bit 和 64bit |
| 18 | +- **Class Pointer**:类型指针,用来指向对象对应的Class对象(其对应的元数据对象)的内存地址,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。在32位系统占4字节,在64位系统中占8字节; |
| 19 | +- **Length**:如果是数组对象,还有一个保存数组长度的空间,占4个字节; |
| 20 | + |
| 21 | +#### 对象实例数据 |
| 22 | + |
| 23 | +对象实例数据包括了对象的所有成员变量,其大小由各个成员变量的大小决定,比如:byte和boolean是1个字节,short和char是2个字节,int和float是4个字节,long和double是8个字节,reference是4个字节(64位系统中是8个字节)。 |
| 24 | + |
| 25 | +| Primitive Type | Memory Required(bytes) | |
| 26 | +| :------------- | :--------------------- | |
| 27 | +| boolean | 1 | |
| 28 | +| byte | 1 | |
| 29 | +| short | 2 | |
| 30 | +| char | 2 | |
| 31 | +| int | 4 | |
| 32 | +| float | 4 | |
| 33 | +| long | 8 | |
| 34 | +| double | 8 | |
| 35 | + |
| 36 | +对于reference类型来说,在32位系统上占用4bytes, 在64位系统上占用8bytes。 |
| 37 | + |
| 38 | +#### 对齐填充 |
| 39 | + |
| 40 | +并不是必然存在的,也没有特别含义,仅仅起到占位符的作用。Java对象占用空间是8字节对齐的,即所有Java对象占用bytes数必须是8的倍数。 |
| 41 | + |
| 42 | + |
| 43 | + |
| 44 | +### 对象的访问定位 |
| 45 | + |
| 46 | +Java程序需要通过栈上的引用数据来操作堆上的具体对象。对象的访问方式取决于虚拟机实现,目前主流的访问方式有**使用句柄**和**直接指针**两种。 |
| 47 | + |
| 48 | +句柄,可以理解为指向指针的指针,维护指向对象的指针变化,而对象的句柄本身不发生变化;指针,指向对象,代表对象的内存地址。 |
| 49 | + |
| 50 | +#### 使用句柄 |
| 51 | + |
| 52 | +Java堆中划分出一块内存来作为句柄池,引用中存储对象的句柄地址,而句柄中包含了对象实例数据与类型数据各自的具体地址信息。 |
| 53 | + |
| 54 | + |
| 55 | + |
| 56 | +优势:引用中存储的是稳定的句柄地址,在对象被移动(垃圾收集时移动对象是非常普遍的行为)时只会改变句柄中的实例数据指针,而引用本身不需要修改。 |
| 57 | + |
| 58 | +#### 直接指针 |
| 59 | + |
| 60 | +如果使用直接指针访问,那么Java堆对象的布局中就必须考虑如何放置访问类型数据的相关信息,而引用中存储的直接就是对象地址。HotSpot 就是使用直接指针的方式进行对象访问的。 |
| 61 | + |
| 62 | + |
| 63 | + |
| 64 | +优势:速度更快,节省了一次指针定位的时间开销。由于对象的访问在Java中非常频繁,因此这类开销积少成多后也是非常可观的执行成本。(例如HotSpot) |
| 65 | + |
| 66 | + |
| 67 | + |
| 68 | + |
| 69 | + |
| 70 | +### 对象的分配过程 |
| 71 | + |
| 72 | +为对象分配内存是一件非常严谨和复杂的任务,JVM 的设计者们不仅需要考虑内存如何分配、在哪里分配等问题,并且由于内存分配算法和内存回收算法密切相关,所以还需要考虑 GC 执行完内存回收后是否会在内存空间中产生内存碎片。 |
| 73 | + |
| 74 | +1. new 的对象先放在伊甸园区,此区有大小限制 |
| 75 | +2. 当伊甸园的空间填满时,程序又需要创建对象,JVM 的垃圾回收器将对伊甸园区进行垃圾回收(Minor GC),将伊甸园区中的不再被其他对象所引用的对象进行销毁。再加载新的对象放到伊甸园区 |
| 76 | +3. 然后将伊甸园中的剩余对象移动到幸存者 0 区 |
| 77 | +4. 如果再次触发垃圾回收,此时会重新放回幸存者 0 区,接着再去幸存者 1 区 |
| 78 | +5. 什么时候才会去养老区呢? 默认是 15 次回收标记 |
| 79 | +6. |
| 80 | + |
| 81 | + |
0 commit comments