在Java中我们创建对象都会用new进行创建,下面我来接收一下new之后对象创建及内存分配的具体的过程
一:虚拟机遇到一条new指令后,先去检查这条指令参数是否能在常量池中定位到一个类的符号引用,并检查这个符号引用代表的类是否已被加载、解析和初始化过,如果没有,那必须先执行相应的类加载过程。
二:类加载检查通过后,接下来虚拟机为新生对象分配内存,因为对象所需内存的大小在类加载后是完全确定的(用引用,堆中存放实例),所以只需分配一个固定大小的内存即可。分配内存的方法用两种(Java虚拟机中)
①指针碰撞:假设Java堆中内存是绝对规整的,所有用过的内存都存放在一边,空闲的内存存在在另一边,中间放着一个以指针为分界点的指示器,分配内存就是把指针往空闲的那侧移动对象需要的内存即可。但是如果Java内存堆不是完整的,就没有办法进行简单的指针碰撞,
②空闲列表:空闲列表就是虚拟机先对内存分块,并用一个表记录每个内存块是否使用的情况,为对象分配内存时只需更新列表上的记录即可,选择哪一种分配方式取决于对中的内存使用情况是否完整。
但是在为对象分配内存的同时,需要考虑其它线程是否也在这一时刻需要分配,因此需要考虑到线程的安全问题,通过以下两种方法进行解决:
①同步处理:一种是对分配内存的空间动作进行同步处理--实际上虚拟机采用CAS配上失败重试的方式保证更新操作的原子性
②本地线程分配缓存TLAB:另一种是把内存分配的动作按照线程在不同的空间之中进行,及每个线程先预先分配一定的缓存。
三:内存分配完成后,虚拟机将分配到的内存空间都初始化为零值(不包括对象头),如果采用的是TLAB的方法分配,则这一步骤在分配之前完成。这一步操作保证了对象的实例字段在Java代码中可以不付初始值就直接使用,程序访问的值为false。这个解决了以前我在上Java课与老师争论在执行构造方法是否会先初始化,明显我对了,但是当时没有拿出有说服力的证据。
四:在上面的步骤完成后,从虚拟机的视角,一个新的对象已经产生了,但是从Java程序的视角来看,对象创建方法才刚刚开始,还要执行对象的init方法(构造方法),一个对象才完成创建
以下是HotSpot解释器的代码片段(C++)
// 确保常量池中存放的是已解释的类
if (!constants->tag_at(index).is_unresolved_klass()) {
// 断言确保是klassOop和instanceKlassOop(这部分下一节介绍)
oop entry = (klassOop) *constants->obj_at_addr(index);
assert(entry->is_klass(), "Should be resolved klass");
klassOop k_entry = (klassOop) entry;
assert(k_entry->klass_part()->oop_is_instance(), "Should be instanceKlass");
instanceKlass* ik = (instanceKlass*) k_entry->klass_part();
// 确保对象所属类型已经经过初始化阶段
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);
}
}
}