赞
踩
HotSpot中对象是是通过对象模型来表示的,具体来说就是oop和klass来表示,oop代表了一个Java对象在堆内存中的表现形式。使用klass来表示Java类的信息。虚拟机创建Java对象时,会在按照klass所提供的类信息,在内存中创建一个oop对象,具体的内存可能是线程TLAB或堆空间。然后该oop对象的地址会赋给一个变量,这个变量可能在线程栈上或直接来自堆空间对此oop的引用。
hotspot/src/share/vm/oops/oop.hpp
HotSpot内部oop的描述如下,它有一个标记头markOop,和一个_metadata联合体指向改对象的Klass。
class oopDesc {
private:
volatile markOop _mark;
union _metadata {
Klass* _klass;
narrowKlass _compressed_klass;
} _metadata;
// Fast access to barrier set. Must be initialized.
static BarrierSet* _bs;
}
hotspot/src/share/vm/oops/markOop.hpp
标记头存储着该对象的Hash,GC年龄,锁状态线程ID等。
class markOopDesc: public oopDesc { private: // Conversion uintptr_t value() const { return (uintptr_t) this; } public: //各标记所占位数 enum { age_bits = 4, lock_bits = 2, biased_lock_bits = 1, max_hash_bits = BitsPerWord - age_bits - lock_bits - biased_lock_bits, hash_bits = max_hash_bits > 31 ? 31 : max_hash_bits, cms_bits = LP64_ONLY(1) NOT_LP64(0), epoch_bits = 2 }; //各标记偏移 enum { lock_shift = 0, biased_lock_shift = lock_bits, age_shift = lock_bits + biased_lock_bits, cms_shift = age_shift + age_bits, hash_shift = cms_shift + cms_bits, epoch_shift = hash_shift }; //计算标记位置 enum { lock_mask = right_n_bits(lock_bits), lock_mask_in_place = lock_mask << lock_shift, biased_lock_mask = right_n_bits(lock_bits + biased_lock_bits), biased_lock_mask_in_place= biased_lock_mask << lock_shift, biased_lock_bit_in_place = 1 << biased_lock_shift, age_mask = right_n_bits(age_bits), age_mask_in_place = age_mask << age_shift, epoch_mask = right_n_bits(epoch_bits), epoch_mask_in_place = epoch_mask << epoch_shift, cms_mask = right_n_bits(cms_bits), cms_mask_in_place = cms_mask << cms_shift #ifndef _WIN64 ,hash_mask = right_n_bits(hash_bits), hash_mask_in_place = (address_word)hash_mask << hash_shift #endif }; //锁对应的值 enum { locked_value = 0, unlocked_value = 1, monitor_value = 2, marked_value = 3, biased_lock_pattern = 5 }; //对象hash enum { no_hash = 0 }; // no hash value assigned enum { no_hash_in_place = (address_word)no_hash << hash_shift, no_lock_in_place = unlocked_value }; //对象GC年龄 enum { max_age = age_mask }; enum { max_bias_epoch = epoch_mask };
hotspot/src/share/vm/oops/klass.hpp
Kalss中是这个对象的部分类描述信息
class Klass : public Metadata { protected: jint _layout_helper; ...... Klass* _super; //父类 Klass* _subklass; //子类 Klass* _next_sibling; //兄弟类 Klass* _next_link; //klass连 ClassLoaderData* _class_loader_data; //同一类加载器加载的类由它管理 jint _modifier_flags; AccessFlags _access_flags; //类访问权限 ..... //虚表长度 int _vtable_len; //跨代引用支持 jbyte _modified_oops; // Card Table Equivalent (YC/CMS support) jbyte _accumulated_modified_oops; // Mod Union Equivalent (CMS support)
hotspot/src/share/vm/oops/instanceKlass.hpp
class InstanceKlass: public Klass { //类状态 enum ClassState { allocated, // allocated (but not yet linked) loaded, // loaded and inserted in class hierarchy (but not linked yet) linked, // successfully linked/verified (but not initialized yet) being_initialized, // currently running class initializer fully_initialized, // initialized (successfull final state) initialization_error // error happened during initialization }; //当前类的对象实例数量 static int number_of_instance_classes() { return _total_instanceKlass_count; } private: static volatile int _total_instanceKlass_count; protected: //类注解信息 Annotations* _annotations; //类包信息 PackageEntry* _package_entry; // Klass* volatile _array_klasses; //类的常量池 ConstantPool* _constants; //内部类 Array<jushort>* _inner_classes; const char* _source_debug_extension; //符号信息 Symbol* _array_name; //字段数量 int _nonstatic_field_size; int _static_field_size; // number words used by static fields (oop and non-oop) in this klass ...... Array<u2>* _fields; }
在Java中创建一个对象时,通常会形如: new Object() ,这个动作翻译成字节码如下:
0: new #2(指向常量池类全限定名索引)
3: dup
4: invokespecial #3(类的<init>初始化方法)
7: astore_1
8: return
hotspot/src/cpu/x86/vm/templateTable_x86.cpp
在模板解释器一篇讲过每一条Java字节码都对应一个指令模板,new对应的指令模板如下,将访问常量池,查找类信息,不存在则走慢分配(slow_case), 存在则走如下分配流程
void TemplateTable::_new() { transition(vtos, atos); //栈顶缓存验证 __ get_unsigned_2_byte_index_at_bcp(rdx, 1); //取new指令后的参数,从当前指令偏移1字节出取得,放入rdx Label slow_case; Label slow_case_no_pop; Label done; Label initialize_header; Label initialize_object; // including clearing the fields Label allocate_shared; //取常量池tags首地址放入rax,常量池首地址放入rcx __ get_cpool_and_tags(rcx, rax); //确保class已在常量池中 const int tags_offset = Array<u1>::base_offset_in_bytes(); __ cmpb(Address(rax, rdx, Address::times_1, tags_offset), JVM_CONSTANT_Class); __ jcc(Assembler::notEqual, slow_case_no_pop); //获取InstanceKlass地址放入rcx __ movptr(rcx, Address(rcx, rdx, Address::times_ptr, sizeof(ConstantPool))); __ push(rcx); // save the contexts of klass for initializing the header //是否解析过 __ cmpb(Address(rcx, InstanceKlass::init_state_offset()), InstanceKlass::fully_initialized); __ jcc(Assembler::notEqual, slow_case); // 取类实例大小,存入rdx寄存器 __ movl(rdx, Address(rcx, Klass::layout_helper_offset())); // 是都有finalizer实现 __ testl(rdx, Klass::_lh_instance_slow_path_bit); __ jcc(Assembler::notZero, slow_case); // // Allocate the instance // 1) 尝试在 TLAB 扽配 // 2) 如果失败则到Eden区分配 // 3) 如果常量池中找不到这个类的Klass,则类还没加载,走slow_case,慢分配 // (creates a new TLAB, etc.) //...... //尝试线程局部分配缓存分配 if (UseTLAB) { // 获取TLAB区剩余空间首地址,放入rax寄存器。 __ movptr(rax, Address(thread, in_bytes(JavaThread::tlab_top_offset()))); //rdx寄存器已经记录了对象大小,此处及根据TLAB空闲区首地址,计算出对象分配后,对象尾地址,放入rbx中 __ lea(rbx, Address(rax, rdx, Address::times_1)); //判断空间是否足够 __ cmpptr(rbx, Address(thread, in_bytes(JavaThread::tlab_end_offset()))); __ jcc(Assembler::above, allow_shared_alloc ? allocate_shared : slow_case); __ movptr(Address(thread, in_bytes(JavaThread::tlab_top_offset())), rbx); //清零操作 if (ZeroTLAB) { // the fields have been already cleared __ jmp(initialize_header); } else { // initialize both the header and fields __ jmp(initialize_object); } } //在Eden取分配 // // rdx: instance size in bytes if (allow_shared_alloc) { __ bind(allocate_shared); //获取Eden区剩余空间的首地址和结束地址。 ExternalAddress heap_top((address)Universe::heap()->top_addr()); ExternalAddress heap_end((address)Universe::heap()->end_addr()); Label retry; __ bind(retry); __ movptr(rax, heap_top); //起始 __ lea(rbx, Address(rax, rdx, Address::times_1)); //计算分配后的起始地址 __ cmpptr(rbx, heap_end); __ jcc(Assembler::above, slow_case); //更新 // rax,: object begin // rbx,: object end // rdx: instance size in bytes __ locked_cmpxchgptr(rbx, heap_top); // if someone beat us on the allocation, try again, otherwise continue __ jcc(Assembler::notEqual, retry); __ incr_allocated_bytes(thread, rdx, 0); } //对象实例数据初始化 if (UseTLAB || Universe::heap()->supports_inline_contig_alloc()) { // The object is initialized before the header. If the object size is // zero, go directly to the header initialization. __ bind(initialize_object); __ decrement(rdx, sizeof(oopDesc)); __ jcc(Assembler::zero, initialize_header); // Initialize topmost object field, divide rdx by 8, check if odd and // test if zero. __ xorl(rcx, rcx); // use zero reg to clear memory (shorter code) __ shrl(rdx, LogBytesPerLong); // divide by 2*oopSize and set carry flag if odd // rdx must have been multiple of 8 #ifdef ASSERT // make sure rdx was multiple of 8 Label L; // Ignore partial flag stall after shrl() since it is debug VM __ jccb(Assembler::carryClear, L); __ stop("object size is not multiple of 2 - adjust this code"); __ bind(L); // rdx must be > 0, no extra check needed here #endif // 按字节进行循环遍历对内存,初始化对象实例内存为零值 { Label loop; __ bind(loop); __ movptr(Address(rax, rdx, Address::times_8, sizeof(oopDesc) - 1*oopSize), rcx); NOT_LP64(__ movptr(Address(rax, rdx, Address::times_8, sizeof(oopDesc) - 2*oopSize), rcx)); __ decrement(rdx); __ jcc(Assembler::notZero, loop); } // 对象头的初始化 __ bind(initialize_header); if (UseBiasedLocking) { __ pop(rcx); // get saved klass back in the register. __ movptr(rbx, Address(rcx, Klass::prototype_header_offset())); __ movptr(Address(rax, oopDesc::mark_offset_in_bytes ()), rbx); } else { __ movptr(Address(rax, oopDesc::mark_offset_in_bytes ()), (intptr_t)markOopDesc::prototype()); // header __ pop(rcx); // get saved klass back in the register. } #ifdef _LP64 __ xorl(rsi, rsi); // use zero reg to clear memory (shorter code) __ store_klass_gap(rax, rsi); // zero klass gap for compressed oops #endif __ store_klass(rax, rcx); // klass { SkipIfEqual skip_if(_masm, &DTraceAllocProbes, 0); // Trigger dtrace event for fastpath __ push(atos); __ call_VM_leaf( CAST_FROM_FN_PTR(address, SharedRuntime::dtrace_object_alloc), rax); __ pop(atos); } __ jmp(done); } // 类没有被加载解析,会跳到此处执行 __ bind(slow_case); __ pop(rcx); // restore stack pointer to what it was when we came in. __ bind(slow_case_no_pop); Register rarg1 = LP64_ONLY(c_rarg1) NOT_LP64(rax); Register rarg2 = LP64_ONLY(c_rarg2) NOT_LP64(rdx); //获取常量池地址,存入rarg1寄存器。 __ get_constant_pool(rarg1); //获取new 指令后操作数,即类在常量池中的索引,放入rarg2寄存器 __ get_unsigned_2_byte_index_at_bcp(rarg2, 1); //进入entry_point,进行类的加载和对象分配,并将分配的对象地址返回,存入rax寄存器中 call_VM(rax, CAST_FROM_FN_PTR(address, InterpreterRuntime::_new), rarg1, rarg2); __ verify_oop(rax); // continue __ bind(done); }
hotspot/src/share/vm/interpreter/interpreterRuntime.cpp
这里走慢分配,会先去加载类,类加载初始化完以后,在执行对象分配
IRT_ENTRY(void, InterpreterRuntime::_new(JavaThread* thread, ConstantPool* pool, int index))
Klass* 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);
//初始化类
klass->initialize(CHECK);
//分配对象
oop obj = klass->allocate_instance(CHECK);
//保存结果,模板中会通过get_vm_result取回结果
thread->set_vm_result(obj);
IRT_END
hotspot/src/share/vm/oops/instanceKlass.cpp
对象的大小在类加载时就已经确定
instanceOop InstanceKlass::allocate_instance(TRAPS) {
int size = size_helper(); // Query before forming handle.
instanceOop i;
i = (instanceOop)CollectedHeap::obj_allocate(h_k, size, CHECK_NULL);
return i;
}
hotspot/src/share/vm/gc/shared/collectedHeap.inline.hpp
oop CollectedHeap::obj_allocate(KlassHandle klass, int size, TRAPS) {
HeapWord* obj = common_mem_allocate_init(klass, size, CHECK_NULL);
return (oop)obj;
}
hotspot/src/share/vm/gc/shared/collectedHeap.inline.hpp
HeapWord* CollectedHeap::common_mem_allocate_init(KlassHandle klass, size_t size, TRAPS) {
HeapWord* obj = common_mem_allocate_noinit(klass, size, CHECK_NULL);
init_obj(obj, size);
return obj;
}
hotspot/src/share/vm/gc/shared/collectedHeap.inline.hpp
慢分配仍然尝试先从TLAB分配,如果TLAB开启了的话,如果没分配到再尝试从堆分配
HeapWord* CollectedHeap::common_mem_allocate_noinit(KlassHandle klass, size_t size, TRAPS) { //从TLAB分配 HeapWord* result = NULL; if (UseTLAB) { result = allocate_from_tlab(klass, THREAD, size); if (result != NULL) { return result; } } //从堆分配 bool gc_overhead_limit_was_exceeded = false; result = Universe::heap()->mem_allocate(size, &gc_overhead_limit_was_exceeded); if (!gc_overhead_limit_was_exceeded) { }
hotspot/src/share/vm/gc/shared/collectedHeap.inline.hpp
HeapWord* CollectedHeap::allocate_from_tlab(KlassHandle klass, Thread* thread, size_t size) {
assert(UseTLAB, "should use UseTLAB");
HeapWord* obj = thread->tlab().allocate(size);
if (obj != NULL) {
return obj;
}
//线程TLAB空间不够,走这里
return allocate_from_tlab_slow(klass, thread, size);
}
hotspot/src/share/vm/gc/shared/collectedHeap.cpp
eapWord* CollectedHeap::allocate_from_tlab_slow(KlassHandle klass, Thread* thread, size_t size) {
//重新计算TLAB大小
size_t new_tlab_size = thread->tlab().compute_size(size);
thread->tlab().clear_before_allocation();
//分配一块新的TLAB
HeapWord* obj = Universe::heap()->allocate_new_tlab(new_tlab_size);
thread->tlab().fill(obj, obj + size, new_tlab_size);
return obj;
}
hotspot/src/share/vm/gc/shared/genCollectedHeap.cpp
为了调试堆分配,使用 -XX:-UseTLAB禁用TLAB分配
HeapWord* GenCollectedHeap::mem_allocate(size_t size,
bool* gc_overhead_limit_was_exceeded) {
return gen_policy()->mem_allocate_work(size,
false /* is_tlab */,
gc_overhead_limit_was_exceeded);
}
hotspot/src/share/vm/gc/shared/collectorPolicy.cpp
堆分配的策略是:
HeapWord* GenCollectorPolicy::mem_allocate_work(size_t size, bool is_tlab, bool* gc_overhead_limit_was_exceeded) { GenCollectedHeap *gch = GenCollectedHeap::heap(); *gc_overhead_limit_was_exceeded = false; HeapWord* result = NULL; // Loop until the allocation is satisfied, or unsatisfied after GC. for (uint try_count = 1, gclocker_stalled_count = 0; /* return or throw */; try_count += 1) { //尝试在年轻代并行无锁分配 Generation *young = gch->young_gen(); if (young->should_allocate(size, is_tlab)) { result = young->par_allocate(size, is_tlab); if (result != NULL) { return result; } } uint gc_count_before; // Read inside the Heap_lock locked region. { MutexLocker ml(Heap_lock); bool first_only = ! should_try_older_generation_allocation(size); //尝试再次分配 result = gch->attempt_allocation(size, is_tlab, first_only); if (GCLocker::is_active_and_needs_gc()) { if (is_tlab) { return NULL; // Caller will retry allocating individual object. } if (!gch->is_maximal_no_gc()) { //尝试扩展堆大小再分配 result = expand_heap_and_allocate(size, is_tlab); // Result could be null if we are out of space. if (result != NULL) { return result; } } } //触发GC VM_GenCollectForAllocation op(size, is_tlab, gc_count_before); VMThread::execute(&op); if (op.prologue_succeeded()) { result = op.result(); if (op.gc_locked()) { assert(result == NULL, "must be NULL if gc_locked() is true"); continue; // Retry and/or stall as necessary. } return result; } }
hotspot/src/share/vm/gc/serial/defNewGeneration.cpp
年轻代
HeapWord* DefNewGeneration::par_allocate(size_t word_size,
bool is_tlab) {
HeapWord* res = eden()->par_allocate(word_size); //最终调用par_allocate_impl
if (CMSEdenChunksRecordAlways && _old_gen != NULL) {
_old_gen->sample_eden_chunk();
}
return res;
}
hotspot/src/share/vm/gc/shared/space.cpp
inline HeapWord* ContiguousSpace::par_allocate_impl(size_t size) {
do {
HeapWord* obj = top();
if (pointer_delta(end(), obj) >= size) {
HeapWord* new_top = obj + size;
HeapWord* result = (HeapWord*)Atomic::cmpxchg_ptr(new_top, top_addr(), obj);
if (result == obj) {
return obj;
}
} else {
return NULL;
}
} while (true);
}
hotspot/src/share/vm/gc/shared/genCollectedHeap.cpp
从年轻代还是老年代尝试,取决于should_allocate,先尝试年轻代
HeapWord* GenCollectedHeap::attempt_allocation(size_t size, bool is_tlab, bool first_only) { HeapWord* res = NULL; if (_young_gen->should_allocate(size, is_tlab)) { res = _young_gen->allocate(size, is_tlab); if (res != NULL || first_only) { return res; } } if (_old_gen->should_allocate(size, is_tlab)) { res = _old_gen->allocate(size, is_tlab); } return res; }
hotspot/src/share/vm/gc/serial/defNewGeneration.cpp
年轻代的Eden空间没分配到,则尝试从form空间分配
HeapWord* DefNewGeneration::allocate(size_t word_size, bool is_tlab) {
HeapWord* result = eden()->par_allocate(word_size);
if (result != NULL) {
if (CMSEdenChunksRecordAlways && _old_gen != NULL) {
_old_gen->sample_eden_chunk();
}
} else {
//从from空间分配
result = allocate_from_space(word_size);
}
return result;
}
TLAB是属于线程的缓存区,因此在分配时不需要加锁同步,使用指针碰撞算法,分配速度较快。因为分配算法的关系,释放时需整块释放,无法按对象释放。在向堆申请时首先会向年轻代申请,申请不到时尝试扩展堆再申请,还是申请不到时,便触发MinorGC。老年代不足时还会触发FullGC。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。