Java对象的分配,根据其过程,将其分为快速分配和慢速分配两种形式,其中快速分配使用无锁的指针碰撞技术在新生代的Eden区上进行分配,而慢速分配根据堆的实现方式、GC的实现方式、代的实现方式不同而具有不同的分配调用层次。
下面就以bytecodeInterpreter解释器对于new指令的解释出发,分析实例对象的内存分配过程:
一、快速分配
1.实例的创建首先需要知道该类型是否被加载和正确解析,根据字节码所指定的CONSTANT_Class_info常量池索引,获取对象的类型信息并调用is_unresovled_klass()验证该类是否被解析过,在创建类的实例之前,必须确保该类型已经被正确加载和解析。
CASE(_new): {
u2 index = Bytes::get_Java_u2(pc+1);
constantPoolOop constants = istate->method()->constants();
if (!constants->tag_at(index).is_unresolved_klass()) {
2.接下来获取该类型在虚拟机中的表示instanceKlass(具体可以参考前文实例探索Java对象的组织结构)
oop entry = constants->slot_at(index).get_oop();
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();
3.当类型已经被初始化并且可以被快速分配时,那么将根据UseTLAB来决定是否使用TLAB技术(Thread-Local Allocation Buffers,线程局部分配缓存技术)来将分配工作交由线程自行完成。TLAB是每个线程在Java堆中预先分配了一小块内存,当有对象创建请求内存分配时,就会在该块内存上进行分配,而不需要在Eden区通过同步控制进行内存分配。
if ( ik->is_initialized() && ik->can_be_fastpath_allocated() ) {
size_t obj_size = ik->size_helper();
oop result = NULL;
// If the TLAB isn't pre-zeroed then we'll have to do it
bool need_zero = !ZeroTLAB;
if (UseTLAB) {
result = (oop) THREAD->tlab().allocate(obj_size);
}
if (result == NULL) {
need_zero = true;
4.如果不使用TLAB或在TLAB上分配失败,则会尝试在堆的Eden区上进行分配。Universe::heap()返回虚拟机内存体系所使用的CollectedHeap,其top_addr()返回的是Eden区空闲块的起始地址变量_top的地址,end_addr()是Eden区空闲块的结束地址变量_end的地址。故这里compare_to是Eden区空闲块的起始地址,new_top为使用该块空闲块进行分配后新的空闲块起始地址。这里使用CAS操作进行空闲块的同步操作,即观察_top的预期值,若与compare_to相同,即没有其他线程操作该变量,则将new_top赋给_top真正成为新的空闲块起始地址值,这种分配技术叫做bump-the-pointer(指针碰撞技术)。
retry:
HeapWord* compare_to = *Universe::heap()->top_addr();
HeapWord* new_top = compare_to + obj_size;
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;
}
}
5.根据是否需要填0选项,对分配空间的对象数据区进行填0
if (result != NULL) {
// Initialize object (if nonzero size and need) and then the header
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);
}
}
6.根据是否使用偏向锁,设置对象头信息,然后设置对象的klassOop引用(这样对象本身就获取了获取类型数据的途径)
if (UseBiasedLocking) {
result->set_mark(ik->prototype_header());
} else {
result->set_mark(markOopDesc::prototype());
}
result->set_klass_gap(0);
result->set_klass(k_entry);
7.把对象地址引入栈,并继续执行下一个字节码
SET_STACK_OBJECT(result, 0);
UPDATE_PC_AND_TOS_AND_CONTINUE(3, 1);
8.若该类型没有被解析,就会调用InterpreterRuntime的_new函数完成慢速分配
// Slow case allocation
CALL_VM(InterpreterRuntime::_new(THREAD, METHOD->constants(), index),
handle_exception);
SET_STACK_OBJECT(THREAD->vm_result(), 0);
THREAD->set_vm_result(NULL);
UPDATE_PC_AND_TOS_AND_CONTINUE(3, 1);
以上就是快速分配的过程,其流程图如下,关键在于快速分配在Eden区所使用的无锁指针碰撞技术
二、慢速分配
接下来看看慢速分配是如何进行的:
1.InterpreterRuntime的_new函数定义在/hotspot/src/share/vm/interpreter/interpreterRuntime.cpp中:
IRT_ENTRY(void, InterpreterRuntime::_new(JavaThread* thread, constantPoolOopDesc* pool, int index))
klassOop k_oop = pool->klass_at(index, CHECK);
instanceKlassHandle klass (THREAD, k_oop);
// Make sure we are not instantiating an abstract klass
klass->check_valid_for_instantiation(true, CHECK);
// Make sure klass is initialized
klass->initialize(CHECK);
oop obj = klass->allocate_instance(CHECK);
thread->set_vm_result(obj);
IRT_END
该函数在进行了对象类的检查(确保不是抽象类)和对该类型进行初始化后,调用instanceKlassHandle的allocate_instance进行内存分配。
其中instanceKlassHandle类由DEF_KLASS_HANDLE宏进行声明,注意该类重载了成员访问运算符”->”,这里的一系列成员方法的访问实际上是instanceKlass对象的访问。
type* operator -> () const { return (type*)obj()->klass_part(); }
2.所以实际上是调用了instanceKlass的allocate_instance()成员函数:
allocate_instance()定义在/hotspot/src/share/vm/oops/instanceKlass.cpp
(1).检查是否设置了Finalizer函数,获取对象所需空间的大小
instanceOop instanceKlass::allocate_instance(TRAPS) {
bool has_finalizer_flag = has_finalizer(); // Query before possible GC
int size = size_helper(); // Query before forming handle.
(2).调用CollectedHeap的obj_allocate()创建一个instanceOop(堆上的对象实例),并根据情况注册Finalizer函数
KlassHandle h_k(THREAD, as_klassOop());
instanceOop i;
i = (instanceOop)CollectedHeap::obj_allocate(h_k, size, CHECK_NULL);
if (has_finalizer_flag && !RegisterFinalizersAtInit) {
i = register_finalizer(i, CHECK_NULL);
}
return i;
3.CollectedHeap::ojb_allocate()定义在/hotspot/src/share/vm/gc_interface/CollectedHeap.hpp中,它将转而调用内联函数obj_allocate()
4.obj_allocate()定义在/hotspot/src/share/vm/gc_interface/CollectedHeap.inline.h中,若当正处于gc状态时,不允许进行内存分配申请,否则将调用common_mem_allocate_init()进行内存分配并返回获得内存的起始地址,随后将调用post_allocation_setup_obj()进行一些初始化工作
oop CollectedHeap::obj_allocate(KlassHandle klass, int size, TRAPS) {
//...assert
HeapWord* obj = common_mem_allocate_init(size, false, CHECK_NULL);
post_allocation_setup_obj(klass, obj, size);
NOT_PRODUCT(Universe::heap()->check_for_bad_heap_word_value(obj, size));
return (oop)obj;
}
5.common_mem_allocate_init()分为两部分,将分别调用common_mem_allocate_noinit()进行内存空间的分配和调用init_obj()进行对象空间的初始化
HeapWord* CollectedHeap::common_mem_allocate_init(size_t size, bool is_noref, TRAPS) {
HeapWord* obj = common_mem_allocate_noinit(size, is_noref, CHECK_NULL);
init_obj(obj, size);
return obj;
}
6.common_mem_allocate_noinit()如下:
(1).若使用了本地线程分配缓冲TLAB,则会调用allocate_from_tlab()尝试从TLAB中分配内存
HeapWord* result = NULL;
if (UseTLAB) {
result = CollectedHeap::allocate_from_tlab(THREAD, size);
if (result != NULL) {
assert(!HAS_PENDING_EXCEPTION,
"Unexpected exception, will result in uninitialized storage");
return result;
}
}
(2).否则会调用堆的mem_allocate()尝试分配
bool gc_overhead_limit_was_exceeded = false;
result = Universe::heap()->mem_allocate(size,
is_noref,
false,
&gc_overhead_limit_was_exceeded);
(3).统计分配的字节数
if (result != NULL) {
//...
THREAD->incr_allocated_bytes(size * HeapWordSize);
return result;
}
(4).否则说明申请失败,若在申请过程中gc没有超时,则抛出OOM异常
if (!gc_overhead_limit_was_exceeded) {
// -XX:+HeapDumpOnOutOfMemoryError and -XX:OnOutOfMemoryError support
report_java_out_of_memory("Java heap space");
if (JvmtiExport::should_post_resource_exhausted()) {
JvmtiExport::post_resource_exhausted(
JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR | JVMTI_RESOURCE_EXHAUSTED_JAVA_HEAP,
"Java heap space");
}
THROW_OOP_0(Universe::out_of_memory_error_java_heap());