在这里我们讨论的对象仅限于普通对象,不包括数组和class
虚拟机遇到一条new指令时,
对象所需内存的大小在类加载完成后便可以完全确定,
为对象分配空间的任务等同于把一块确定大小的内存从java堆中划分出来。 假设java堆中内存是绝对规整的,所有用过的内存都放在一边,空闲的内存放在另一边,中间放着一个指针作为分界点的指示器,那所分配的内存就仅仅是把那个指针向空闲那边挪动一段与对象大小相等的距离,这种分配方式称为“指针碰撞(Bump the Pointer)”。
如果java堆中的内存不是规整的,已使用的内存和空闲的内存相互交错,那就没办法简单的进行指针碰撞了,虚拟机就必须维护一个列表,记录上那些内存块是可用的,在分配的时候从列表中找到一块足够大的空间划分给对象实例,并更新列表上的记录,这种分配方式成为“空闲列表(Free List)”。
如何判断java堆是否规整:是由采用的垃圾收集器是否带有压缩整理功能决定的。因此,在使用Serial、ParNew等带Compact过程的收集器时,系统采用的分配算法是指针碰撞,而使用CMS这种基于Mark-Sweep算法的收集器时,通常采用空闲列表。
如果使用TLAB,这一工作过程也可以提前至TLAB分配是进行。这一步操作保证了对象的实例字段在java代码中可不赋初始值就直接使用,程序能访问到这些字段的数据类型所对应的零值。
例如这个对象是哪个类的实例,如何才能找到类的元数据信息、对象的哈希码、对象的GC分代年龄等信息。这些信息存放在对象的对象头(Object Header)之中。根据虚拟机当前运行的状态的不同,如是否启用偏向锁等,对象头会有不同的设置方式。
在HotSpot虚拟机中,对象在内存中存储的布局可以分为3块区域 对象头(Header) 实例数据(Instance) 对象填充(Padding)
其中对象头主要包含两部分的信息, 一部分是对象自身的运行时数据, 例如hashcode、GC年代、锁状态、线程持有的锁、偏向线程的ID、偏向时间戳等 这部分的数据在32位虚拟机和64位虚拟机上的大小分别也是32位和64位,也称作Mark Word
另一部分数据是类型指针,即指向这个对象的类的元数据, 虚拟机可以根据类型指针知道这个对象是哪个类的实例。 当然不是所有的虚拟机都实现在对象头数据上保留类型指针, 也就是说查找对象的元数据不一定要经过对象头本身,这个后面再说。
如果这个对象是一个数组,那么对象头还会包括一个记录数组长度的数据, 因为虚拟机只能通过普通java对象的元数据确定对象的大小,但是不能通过数组的元数据确定数组的大小,
这里引出一个概念 元数据,什么是元数据呢
https://www.zhihu.com/question/20679872/answer/65565699 http://www.ruanyifeng.com/blog/2007/03/metadata.html
总的来说 元数据是用来描述数据的数据
【参考资料】
1、《The Java Virtual Machine Specification》Java SE 8 Edition:https://docs.oracle.com/javase/specs/jvms/se8/html/index.html
2、《Java Platform, Standard Edition HotSpot Virtual Machine Garbage Collection Tuning Guide》:http://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/index.html
3、《Memory Management in the Java HotSpot™ Virtual Machine》:http://www.oracle.com/technetwork/java/javase/tech/memorymanagement-whitepaper-1-150020.pdf
4、HotSpot虚拟机参数官方说明:http://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html
5、《深入理解Java虚拟机:JVM高级特性与最佳实践》第二版 第2章