读深入理解JVM虚拟机-new/对象创建
背景
- 作为java程序猿很好奇:实用很频繁的new指令,jvm在new的时候是怎么样的一个过程.
过程简述
加载类信息
- 在遇到一个new指令的时候,必须要先检查这个new指令的的参数能否在<font color=red>
常量池
</font>中定位一个类的符号引用,并检查,这个类是否已经被加载、解析、初始化.
分配内存
- 任何对象的内存分配都在<font color=red>
类加载完成
后</font>便可完全确定. - 然后分配的时候,拿这一块确定大小的内存在
java堆
中划分出来.
两种方法
指针碰撞
- 当java堆
内存是规整的
,空闲内存和使用过的内存放在两侧,中间有个指针进行作为分界点的指示器.分配内存移动指针就够了,这种分配方式叫做<font color=red>指针碰撞
</font>
空闲列表
- 如果java堆内存并不规整,空闲内存和使用过的内存互相交错,那么java堆必须维护一个列表(记录那些内存可用),分配时找到足够大的内存分配给对象实例,并且记录在列表中,这种分配方式叫做<font color=red>
空闲列表
</font>
线程安全问题
- new指令是非常频繁的指令,即时是一个简单的移动指针,也会遇到线程安全问题.
CAS(COMPARE AND SWAP)
- 以后会详述
TLAB(Thread LOCAL ALLOCATION BUFFER)
- 为了解决线程安全问题,每个线程,在java堆中预先分配一部分内存,称之为本地线程分配缓冲,哪个线程需要分配内存,就在哪个线程的TLAB上分配,<font color=red>只有在TLAB用完了,并分配新的TLAB时才会同步锁定.</font>
- 通过-XX:+/-UseTLAB参数来设定
- 如果使用此方式,那么下一步的初始化零值,可以在TLAB中完成
初始化零值
- jvm需要将分配的存储空间(不包括对象头),这以操作保证了对象的实例字段在java代码中可以不赋初始值就可以直接使用,程序能访问到的这些字段的数据类型对应的零值.
设置对象头(object header)
- 后面会进行详细介绍
执行init方法
- 经历过上述步骤之后,对象就可以使用了,但是所有的字段都是零值.所以执行new指令之后,会执行init方法,把对象按照程序员的意愿进行初始化,这样一个真正可用的对象才算完全产生.
程序
- hotspot/bytecodeinterpreter.cpp
//确认常量池中存放的是已经解释的类
if (!constants->tag_at(index).is_unresolved_klass()) {
// 断言确保是klassOop和instanceKlassOop
Klass* entry = constants->slot_at(index).get_klass();
assert(entry->is_klass(), "Should be resolved klass");
Klass* k_entry = (Klass*) entry;
assert(k_entry->oop_is_instance(), "Should be InstanceKlass");
InstanceKlass* ik = (InstanceKlass*) k_entry;
//确保对象所属类型已经经过类初始化阶段
if ( ik->is_initialized() && ik->can_be_fastpath_allocated() ) {
//取对象长度
size_t obj_size = ik->size_helper();
oop result = NULL;
// 记录是否需要将对象所有的字段置零
bool need_zero = !ZeroTLAB;
//是否在TLAB中分配对象
if (UseTLAB) {
result = (oop) THREAD->tlab().allocate(obj_size);
}
if (result == NULL) {
need_zero = true;
//尝试分配到eden区中分配对象
retry:
HeapWord* compare_to = *Universe::heap()->top_addr();
HeapWord* new_top = compare_to + obj_size;
//cmpxchg是x86中的CAS指令,这里是一个C++方法,通过CAS方法分配空间,如果失败,那么赚到retry重试,知道分配成功
if (new_top <= *Universe::heap()->end_addr()) {
if (Atomic::cmpxchg_ptr(new_top, Universe::heap()->top_addr(), compare_to) != compare_to) {
goto retry;
}
result = (oop) compare_to;
}
}
if (result != NULL) {
// 如果需要,则为对象初始化零值
if (need_zero ) {
HeapWord* to_zero = (HeapWord*) result + sizeof(oopDesc) / oopSize;
obj_size -= sizeof(oopDesc) / oopSize;
if (obj_size > 0 ) {
memset(to_zero, 0, obj_size * HeapWordSize);
}
}
//根据是否启用偏向所来设置对象头信息
if (UseBiasedLocking) {
result->set_mark(ik->prototype_header());
} else {
result->set_mark(markOopDesc::prototype());
}
result->set_klass_gap(0);
result->set_klass(k_entry);
//将对象引用入栈,继续执行下一条指令
SET_STACK_OBJECT(result, 0);
UPDATE_PC_AND_TOS_AND_CONTINUE(3, 1);
}
}
}
// Slow case allocation