一、对象的实例化
对象实例化步骤:
- 首先加载对象所属类的相关信息,若该类存在父类,那么要将父类的信息也加载进来,依此类推
- 接着在堆中为对象分配内存,有两种分配方法:当堆内存空间较为规整时,采用指针碰撞法;若堆内存空间不规整,则使用空闲列表法
- 随后对对象的变量进行默认赋值,按照类中变量声明的顺序进行
- 再为对象设置对象头,对象头包含对象所属类在方法区中的地址、对象的哈希值、分代年龄、锁状态标志等信息
- 最后依次调用构造器进行属性赋值,先调用父类的构造器,再调用子类的构造器
二、直接内存(Direct Memory)
- 直接内存不在 JVM 堆内存中,它是向操作系统申请的内存,虽不受 JVM 堆内存管理机制的管控,但受系统内存(物理内存、本地内存)限制,使用时不能超出操作系统使用时不能超出操作系统可用内存
- 可通过 Java NIO 操作直接内存,这能显著提升 I/O 速度。传统 I/O 需先将数据从外存(硬盘)读入本地内存,再从本地内存复制到 JVM 堆内存;而 NIO 可直接操作直接内存,数据只需从外存读到直接内存,减少了数据拷贝步骤,从而提升了效率
三、其它相关知识
(1)指针碰撞(Bump the Pointer)
- 适用场景:指针碰撞适用于堆内存空间规整的情况。所谓空间规整,就是已使用的内存和未使用的内存被分隔开,中间有一个分界指针。在这种情况下,JVM 只需要移动这个分界指针,就可以为新对象分配内存
- 工作原理:当有新对象需要分配内存时,JVM 会将分界指针向空闲内存区域移动与对象大小相等的距离,这个移动后的位置就是新对象的内存地址。简单来说,就是指针不断地 “碰撞” 着向空闲内存区域推进,为新对象分配空间
- 示例说明:假设堆内存的起始地址是 0,已使用的内存到地址 100,分界指针指向 100。现在要创建一个大小为 20 的对象,JVM 会将分界指针从 100 移动到 120,新对象就被分配在地址 100 - 119 的空间中
(2)空闲列表分配(Free List Allocation)
- 适用场景:空闲列表分配适用于堆内存空间不规整的情况。当堆中存在大量不连续的空闲内存块时,无法使用指针碰撞的方式进行内存分配,此时就需要使用空闲列表
- 工作原理:JVM 会维护一个空闲列表,记录堆中所有空闲内存块的信息,包括起始地址和大小。当有新对象需要分配内存时,JVM 会从空闲列表中查找一个足够大的空闲内存块分配给对象。如果该空闲内存块比对象所需的内存大,还会将剩余的空闲内存块重新记录到空闲列表中
- 示例说明:假设空闲列表中记录了三个空闲内存块:[起始地址 100,大小 50]、[起始地址 200,大小 100]、[起始地址 350,大小 80]。现在要创建一个大小为 60 的对象,JVM 会选择第二个空闲内存块(起始地址 200,大小 100),将对象分配在地址 200 - 259 的空间中,剩余的地址 260 - 299 作为新的空闲内存块重新记录到空闲列表中