1. 对象的创建
虚拟机遇到一个new指令时
- 检查这个指令的参数是否能在常量池定位到一个类的符号引用,并且检查这个符号引用代表的类是否已经被加载、解析和初始化过。如果没有,则必须先执行相应的类加载过程。
- 对新生对象分配内存
- 内存是绝对规整的:
Bump the Pointer指针碰撞。就是仅仅把内存分界指针挪动一段与对象大小相等的距离。 - 内存不是规整的:
Free List通过空闲列表中找到足够大的空闲内存
- 内存是绝对规整的:
- 将分配到的内存空间初始化为0(不包括对象头)
- 虚拟机要对对象进行必要的设置,例如这个对象是哪个类的实例、如何才能找到类的元数据信息、对象的哈希码、对象的GC分代年龄等信息。这些都存储在对象头(Object Head)中
关于内存分配
| GC收集器 | 内存分配算法 |
|---|---|
| Serial、ParNew(带Compact过程的收集器) | 指针碰撞 |
| CMS(基于Mark-Sweep) | Free List |
问题:对象创建在虚拟机中是非常频繁的,即使是仅仅修改一个指针,在并发的情况下也不一定是安全的。
解决:
- 同步处理:CAS
- 将内存分配的操作划分在不同的空间中,即每个线程在Java堆中预先分配一小块内存,成为本地线程分配缓冲(Thread Local Allocation Buffer,TLAB)
虚拟机对TLAB使用-XX:+/-UseTLAB参数来配置
2. 对象的内存布局
- 对象头(Object Header)
- 实例数据(Instance Data)
- 对齐填充(Padding)

对象的访问定位
建立对象是为了使用,我们在Java程序中使用栈上的reference数据来操作堆上的具体对象。Java在虚拟机规范中只规定了一个指向对象的引用,并没有定义这个引用应该通过何种方式去定位、访问堆中的具体位置,所以这是取决于虚拟机实现而定的。目前主流方式有两种:
句柄
Java堆中将会划分中一块内存来作为句柄池,reference中存储的就是对象的句柄地址,而句柄包含了对象实例数据与类型数据各自的地址信息。好处是移动对象时,只需要移动句柄,而不是reference。
直接指针
堆中对象实例包含了类型数据的指针信息,reference中存储的就是对象实例地址。