参考 《深入理解Java虚拟机 JVM高级特效与最佳实践》 2.3
1.Object object = new Object();
- 虚拟机遇到一条new指令时,先去常量池里检查有没有目标类名的符号引用,并且检查这个符号引用代表的类是否已经被加载,如果没有被加载、解析、初始化过,则进行加载、解析、初始化。
- 类加载检查通过后,虚拟机开始为对象分配内存。
- 对象所需的内存大小在类加载之后就可以完全确定,为对象分配内存其实就是划分一块确定大小的内存出来。
- 划分内存有两种方式:
1.Java堆中内存绝对规整的,用过的内存在一边,没用的在另一边,这样的话划分内存就是简单地把分割两块区域的指针移动一个确定的长度就好了;
2.Java堆中内存是不规整的,虚拟机就需要维护一个列表,上面记录哪些内存有用,哪些没用,分配内存的时候就是要在这个列表中寻找一块足够大的,能塞的下目标对象的内存,然后把对象放进去。 - 关于分配内存时线程安全的问题,有两种方案:
1.使用同步的方式来分配内存
2.为需要分配内存的线程们各自给一块堆内存用来分配,这样线程之间就不会打架了。这块内存叫TLAB(Thread Local Allocation Buffer),只有TLAB用完并分配新的TLAB的时候,才需要同步锁同步。 - 内存分配完后,虚拟机将分配到的内存初始化为零值(不包括对象头)。
- 接下来设置对象头,对象头里就是一些对象的元信息。
- 开始调用构造函数,虚拟机对应的方法为<init>方法
- 调用完构造函数之后,一个Java对象创建完毕
2.对象内存布局
- 一个对象在内存中的布局分三块
1.对象头
2.实例数据
3.对齐填充 - 对象头分为两块
1.对象运行时数据:如哈希码、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等。
2.类型指针:指明这个对象的类型信息。不过类型信息不一定在对象头中,只有采用了直接指针方式作为引用的虚拟机会含有类型指针,而采用句柄方式的则类型信息在句柄中。
3.如果对象是数组类型,则还会包含记录数组长度的数据。 - 实例数据
也就是程序员定义的各个字段了。 - 对齐填充
只是起占位符发作用。
3.对象的访问定位
- Java中访问定位其实就是通过引用来定位,但是具体怎么通过引用来定位,虚拟机规范没有定死。只要能通过引用找到实际的对象即可。
-
有两种方式定位对象
1.句柄:Java堆中会分出一块区域作为句柄池,引用中储存的就是句柄的地址,句柄中包含了对象实例的地址和对象类型信息的地址。
2.直接指针:引用中存放的就是对象的地址,这样的话就要考虑如何在对象中存放类型信息了。
- 句柄引用和指针引用各自的优缺点
1.句柄的优点:由于引用中存放的句柄的地址,所以句柄中对象地址的改变不会影响引用的值。
2.直接指针的优点:减少了一次指针定位的开销,Hotspot是采用指针引用的。