HotSpot 对象的创建

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), 存在则走如下分配流程

  1. 尝试线程局部分配缓存分配
  2. 失败,在Eden区分配
  3. 对象实例数据初始化
  4. 对象头的初始化
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
堆分配的策略是:

  1. 先尝试在年轻代并行无锁分配
  2. 分配失败,在尝试有锁状态下分配
  3. 还失败,再扩展堆空间再试
  4. 最终没有分配到则触发GC
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。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值