在java编程中,我们需要一个类的实例时,只需要new一个对象就得到了这个实例,然后对实例对象进行操作,那么当我们new对象时虚拟机又会进行什么操作呢?
java虚拟机遇到一条new指令时,就会为这个对象分配内存,即把一块确定大小的(类加载完成后就已经确定该对象的所需内存大小)内存空间从java堆中划分出来。
内存分配的两种方式:
1.指针碰撞
2.空闲列表
下面引自《深入理解java虚拟机_JVM高级特性与最佳实践》
在说完内存分配方式之后,我们来说一下对象在内存中的存储布局:
在HotSpot虚拟机中,对象可分为3个区域:对象头、实例数据、对象填充。
对象头包含两部分:第一部分是存储对象自身的运行时数据(如哈希码、GC分代年龄、锁标志位、偏向线程Id等),第二部分是类型指针,指向它的类元数据,虚拟机通过这个指针来确定这个对象时哪个类的实例。
实例数据域是对象真正存储的有效信息,也是程序代码中定义的各种类型的字段内容(包含父类的)。
对齐填充不是必须存在的,只是起占位符作用。对象的大小必须是8字节的整数倍,因此当对象实例数据域没有对齐时,就需要 对齐填充来补全。
对象的访问定位:
内存分配完毕,对象就创建成功啦,那么如何去使用对象呢?这就需要用到类的引用变量了。java程序需要用到栈上所保存reference数据来操作堆上的具体对象。通过这个reference可以定位并访问到堆上创建的对象。而对象的访问方式由虚拟机决定,目前比较主流的两种访问方式是:使用句柄和直接指针两种方式。
使用句柄访问的话,堆中会划分出一块内存作为句柄池,引用中就存放的是句柄地址,可理解为指针,句柄中则包含了对象实例数据与类型数据的地址。(类型数据个人理解为对象所对应的类,如果有错请指正)
直接指针即是引用中存放对象地址,不过对象的布局中需要考虑如何放置指向对象类型数据的地址。