国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發(fā)設計 > 正文

源碼分析:Java對象的內(nèi)存分配

2019-11-14 15:04:49
字體:
供稿:網(wǎng)友

  java對象的分配,根據(jù)其過程,將其分為快速分配和慢速分配兩種形式,其中快速分配使用無鎖的指針碰撞技術在新生代的Eden區(qū)上進行分配,而慢速分配根據(jù)堆的實現(xiàn)方式、GC的實現(xiàn)方式、代的實現(xiàn)方式不同而具有不同的分配調(diào)用層次。 
下面就以bytecodeInterPReter解釋器對于new指令的解釋出發(fā),分析實例對象的內(nèi)存分配過程:

 一、快速分配

  1.實例的創(chuàng)建首先需要知道該類型是否被加載和正確解析,根據(jù)字節(jié)碼所指定的CONSTANT_Class_info常量池索引,獲取對象的類型信息并調(diào)用is_unresovled_klass()驗證該類是否被解析過,在創(chuàng)建類的實例之前,必須確保該類型已經(jīng)被正確加載和解析。

 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對象的組織結(jié)構(gòu)) 

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.當類型已經(jīng)被初始化并且可以被快速分配時,那么將根據(jù)UseTLAB來決定是否使用TLAB技術(Thread-Local Allocation Buffers,線程局部分配緩存技術)來將分配工作交由線程自行完成。TLAB是每個線程在Java堆中預先分配了一小塊內(nèi)存,當有對象創(chuàng)建請求內(nèi)存分配時,就會在該塊內(nèi)存上進行分配,而不需要在Eden區(qū)通過同步控制進行內(nèi)存分配。

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區(qū)上進行分配。Universe::heap()返回虛擬機內(nèi)存體系所使用的CollectedHeap,其top_addr()返回的是Eden區(qū)空閑塊的起始地址變量_top的地址,end_addr()是Eden區(qū)空閑塊的結(jié)束地址變量_end的地址。故這里compare_to是Eden區(qū)空閑塊的起始地址,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.根據(jù)是否需要填0選項,對分配空間的對象數(shù)據(jù)區(qū)進行填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.根據(jù)是否使用偏向鎖,設置對象頭信息,然后設置對象的klassOop引用(這樣對象本身就獲取了獲取類型數(shù)據(jù)的途徑)

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.把對象地址引入棧,并繼續(xù)執(zhí)行下一個字節(jié)碼

SET_STACK_OBJECT(result, 0);              UPDATE_PC_AND_TOS_AND_CONTINUE(3, 1);

  8.若該類型沒有被解析,就會調(diào)用InterpreterRuntime的_new函數(shù)完成慢速分配

// 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區(qū)所使用的無鎖指針碰撞技術 

    

 二、慢速分配

  接下來看看慢速分配是如何進行的: 
  1.InterpreterRuntime的_new函數(shù)定義在/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

  該函數(shù)在進行了對象類的檢查(確保不是抽象類)和對該類型進行初始化后,調(diào)用instanceKlassHandle的allocate_instance進行內(nèi)存分配。 
  其中instanceKlassHandle類由DEF_KLASS_HANDLE宏進行聲明,注意該類重載了成員訪問運算符”->”,這里的一系列成員方法的訪問實際上是instanceKlass對象的訪問。

 type*    Operator -> () const       { return (type*)obj()->klass_part(); }

  2.所以實際上是調(diào)用了instanceKlass的allocate_instance()成員函數(shù): 
  allocate_instance()定義在/hotspot/src/share/vm/oops/instanceKlass.cpp 
  (1).檢查是否設置了Finalizer函數(shù),獲取對象所需空間的大小

instanceOop instanceKlass::allocate_instance(TRAPS) {     bool has_finalizer_flag = has_finalizer(); // Query before possible GC     int size = size_helper();  // Query before forming handle.

  (2).調(diào)用CollectedHeap的obj_allocate()創(chuàng)建一個instanceOop(堆上的對象實例),并根據(jù)情況注冊Finalizer函數(shù)

    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中,它將轉(zhuǎn)而調(diào)用內(nèi)聯(lián)函數(shù)obj_allocate()

  4.obj_allocate()定義在/hotspot/src/share/vm/gc_interface/CollectedHeap.inline.h中,若當正處于gc狀態(tài)時,不允許進行內(nèi)存分配申請,否則將調(diào)用common_mem_allocate_init()進行內(nèi)存分配并返回獲得內(nèi)存的起始地址,隨后將調(diào)用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()分為兩部分,將分別調(diào)用common_mem_allocate_noinit()進行內(nèi)存空間的分配和調(diào)用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,則會調(diào)用allocate_from_tlab()嘗試從TLAB中分配內(nèi)存

  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).否則會調(diào)用堆的mem_allocate()嘗試分配

 

  bool gc_overhead_limit_was_exceeded = false;  result = Universe::heap()->mem_allocate(size,                                          is_noref,                                          false,                                          &gc_overhead_limit_was_exceeded);

  (3).統(tǒng)計分配的字節(jié)數(shù)

 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());

  7.對象內(nèi)存分配后的初始化過程包括兩部分,一個是init_obj()完成對對象內(nèi)存空間的對齊和填充,一個是post_allocation_setup_obj()對堆上的oop對象進行初始化。 

  (1).init_obj():

void CollectedHeap::init_obj(HeapWord* obj, size_t size) {  assert(obj != NULL, "cannot initialize NULL object");  const size_t hs = oopDesc::header_size();  assert(size >= hs, "unexpected object size");  ((oop)obj)->set_klass_gap(0);  Copy::fill_to_aligned_words(obj + hs, size - hs);}

  hs就是對象頭的大小,fill_to_aligned_words將對象空間除去對象頭的部分做填0處理,該函數(shù)定義在/hotspot/src/share/vm/utilities/copy.h中,并轉(zhuǎn)而調(diào)用pd_fill_to_aligned_words()。 
  pd_fill_to_aligned_words根據(jù)不同平臺實現(xiàn),以x86平臺為例,該函數(shù)定義在/hotspot/src/cpu/x86/vm/copy_x86.h中:

static void pd_fill_to_words(HeapWord* tohw, size_t count, juint value) {#ifdef AMD64  julong* to = (julong*) tohw;  julong  v  = ((julong) value << 32) | value;  while (count-- > 0) {    *to++ = v;  }#else  juint* to = (juint*)tohw;  count *= HeapWordSize / BytesPerInt;  while (count-- > 0) {    *to++ = value;  }#endif // AMD64}

  該函數(shù)的作用就是先將地址類型轉(zhuǎn)換,然后把堆的字數(shù)轉(zhuǎn)化為字節(jié)數(shù),再對該段內(nèi)存進行填值(value = 0)處理

  (2).post_allocation_setup_obj()調(diào)用了post_allocation_setup_common()進行初始化工作,然后調(diào)用post_allocation_notify()通知JVMTI和dtrace
  

void CollectedHeap::post_allocation_setup_obj(KlassHandle klass,                                              HeapWord* obj,                                              size_t size) {  post_allocation_setup_common(klass, obj, size);  assert(Universe::is_bootstrapping() ||         !((oop)obj)->blueprint()->oop_is_array(), "must not be an array");  // notify jvmti and dtrace  post_allocation_notify(klass, (oop)obj);}

  post_allocation_setup_common()如下:

void CollectedHeap::post_allocation_setup_common(KlassHandle klass,                                                 HeapWord* obj,                                                 size_t size) {  post_allocation_setup_no_klass_install(klass, obj, size);  post_allocation_install_obj_klass(klass, oop(obj), (int) size);}

  post_allocation_setup_no_klass_install()根據(jù)是否使用偏向鎖,設置對象頭信息等,即初始化oop的_mark字段。post_allocation_install_obj_klass()設置對象實例的klassOop引用,即初始化oop的_metadata(_klass/_compressed_klass)字段 。

  以上內(nèi)容就是堆實現(xiàn)無關的慢速分配過程,其流程圖如下: 

        

 三、堆的分配實現(xiàn)

  1.mem_allocate將由堆的實現(xiàn)類型定義,以GenCollectedHeap為例:

HeapWord* GenCollectedHeap::mem_allocate(size_t size,                                         bool is_large_noref,                                         bool is_tlab,                                         bool* gc_overhead_limit_was_exceeded) {  return collector_policy()->mem_allocate_work(size,                                               is_tlab,                                               gc_overhead_limit_was_exceeded);}

  2.由之前分析,GenCollectedHeap根據(jù)用戶配置有著不同的GC策略(默認的和配置UseSerialGC的MarkSweepPolicy、配置UseComcMarkSweepGC和UseAdaptiveSizePolicy的ASConcurrentMarkSweepPolicy、只配置UseComcMarkSweepGC的ConcurrentMarkSweepPolicy),但這里,對象內(nèi)存空間的基本結(jié)構(gòu)和分配的思想是一致的,所以統(tǒng)一由GenCollectorPolicy實現(xiàn)進行分代層級的對象分配操作,但具體的工作將交由各代的實現(xiàn)者來完成。

  GenCollectedPolicy的mem_allocate_work()函數(shù)如下: 
  (1).gch指向GenCollectedHeap堆,內(nèi)存分配請求將循環(huán)不斷地進行嘗試,直到分配成功或GC后分配失敗

HeapWord* GenCollectorPolicy::mem_allocate_work(size_t size,                                        bool is_tlab,                                        bool* gc_overhead_limit_was_exceeded) {  GenCollectedHeap *gch = GenCollectedHeap::heap();  //...  // Loop until the allocation is satisified,  // or unsatisfied after GC.  for (int try_count = 1; /* return or throw */; try_count += 1) {  

  對于占用空間比較大的對象,如果經(jīng)常放在新生代,那么剩余的內(nèi)存空間就會非常緊張,將可能會導致新生代內(nèi)存垃圾回收的頻繁觸發(fā)。故若對象的大小超過一定值,那么就不應該分配在新生代。

   //...緊接上面部分   HandleMark hm; // discard any handles allocated in each iteration    // First allocation attempt is lock-free.    Generation *gen0 = gch->get_gen(0);    if (gen0->should_allocate(size, is_tlab)) {      result = gen0->par_allocate(size, is_tlab);      if (result != NULL) {        assert(gch->is_in_reserved(result), "result not in heap");        return result;      }    }

  若對象應該在新生代上分配,就會調(diào)用新生代的par_allocate()進行分配,注意在新生代普遍是采用復制收集器的,而內(nèi)存的分配對應采用了無鎖式的指針碰撞技術。

  (2).在新生代上嘗試無鎖式的分配失敗,那么就獲取堆的互斥鎖,并嘗試在各代空間內(nèi)進行內(nèi)存分配

unsigned int 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 (result != NULL) {        assert(gch->is_in_reserved(result), "result not in heap");        return result;      }

  其中should_try_older_generation_allocation()如下:

bool GenCollectorPolicy::should_try_older_generation_allocation(        size_t word_size) const {  GenCollectedHeap* gch = GenCollectedHeap::heap();  size_t gen0_capacity = gch->get_gen(0)->capacity_before_gc();  return    (word_size > heap_word_size(gen0_capacity))         || GC_locker::is_active_and_needs_gc()         || gch->incremental_collection_failed();}

  當進行gc前,新生代的空閑空間大小不足以分配對象,或者有線程觸發(fā)了gc,或前一次的FullGC是由MinorGC觸發(fā)的情況,都應該不再嘗試再更高的內(nèi)存代上進行分配,以保證新分配的對象盡可能在新生代空間上。 

  attempt_allocation()實現(xiàn)如下:

HeapWord* GenCollectedHeap::attempt_allocation(size_t size,                                               bool is_tlab,                                               bool first_only) {  HeapWord* res;  for (int i = 0; i < _n_gens; i++) {    if (_gens[i]->should_allocate(size, is_tlab)) {      res = _gens[i]->allocate(size, is_tlab);      if (res != NULL) return res;      else if (first_only) break;    }  }  // Otherwise...  return NULL;}

  即由低內(nèi)存代向高內(nèi)存代嘗試分配內(nèi)存 

  (3).從各個代空間都找不到可用的空閑內(nèi)存(或不應該在更高的內(nèi)存代上分配時),如果已經(jīng)有線程觸發(fā)了gc,那么當各代空間還有virtual space可擴展空間可用時,將會嘗試擴展代空間并再次嘗試進行內(nèi)存分配,有點在gc前想盡一切辦法獲得內(nèi)存的意思。

if (GC_locker::is_active_and_needs_gc()) {        if (is_tlab) {          return NULL;  // Caller will retry allocating individual object        }        if (!gch->is_maximal_no_gc()) {          // Try and expand heap to satisfy request          result = expand_heap_and_allocate(size, is_tlab);          // result could be null if we are out of space          if (result != NULL) {            return result;          }        }

  (4).否則各代已經(jīng)沒有可用的可擴展空間時,當當前線程沒有位于jni的臨界區(qū)時,將釋放堆的互斥鎖,以使得請求gc的線程可以進行gc操作,等待所有本地線程退出臨界區(qū)和gc完成后,將繼續(xù)循環(huán)嘗試進行對象的內(nèi)存分配

JavaThread* jthr = JavaThread::current();        if (!jthr->in_critical()) {          MutexUnlocker mul(Heap_lock);          // Wait for JNI critical section to be exited          GC_locker::stall_until_clear();          continue;        }

  (5).若各代無法分配對象的內(nèi)存,并且沒有gc被觸發(fā),那么當前請求內(nèi)存分配的線程將發(fā)起一次gc,這里將提交給VM一個GenCollectForAllocation操作以觸發(fā)gc,當操作執(zhí)行成功并返回時,若gc鎖已被獲得,那么說明已經(jīng)由其他線程觸發(fā)了gc,將繼續(xù)循環(huán)以等待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      }

  否則將等待gc完成,若gc超時則會將gc_overhead_limit_was_exceeded設置為true返回給調(diào)用者,并重置超時狀態(tài),并對分配的對象進行填充處理

    const bool limit_exceeded = size_policy()->gc_overhead_limit_exceeded();      const bool softrefs_clear = all_soft_refs_clear();      assert(!limit_exceeded || softrefs_clear, "Should have been cleared");      if (limit_exceeded && softrefs_clear) {        *gc_overhead_limit_was_exceeded = true;        size_policy()->set_gc_overhead_limit_exceeded(false);        if (op.result() != NULL) {          CollectedHeap::fill_with_object(op.result(), size);        }        return NULL;      }

  以上內(nèi)容就是堆的實現(xiàn)相關、但代/GC實現(xiàn)無關的分配過程,其流程圖歸納如下: 

        

 


發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 莒南县| 克拉玛依市| 师宗县| 峨眉山市| 信丰县| 珠海市| 泗阳县| 苍溪县| 沙雅县| 沭阳县| 高雄县| 崇礼县| 新丰县| 瑞安市| 凭祥市| 孝义市| 百色市| 元朗区| 岢岚县| 周宁县| 荣成市| 密山市| 苍溪县| 襄汾县| 平武县| 乐业县| 南陵县| 体育| 张掖市| 丰宁| 玛曲县| 永福县| 安吉县| 九龙城区| 石首市| 永德县| 墨竹工卡县| 齐齐哈尔市| 紫金县| 依安县| 博客|