內存堆Gc時公認的根對象

    內存堆的Gc就是回收內存堆中垃圾對象(非active對象),那麼這裏就有一個問題了,如何尋找垃圾對象?換個思路來解,就是如何找到所有的active的對象,那麼剩下的就是垃圾對象了.HotSpot是通過首先找到所謂的根對象,然後根據這些根對象遞歸或迭代的搜索所有的引用對象,而找到的這些個對象就是所謂的active對象了.其實,Gc時的根對象是一個與運行時上下文相關的概念,以基於內存分代管理的內存代管理器來講,當對某一個內存代驚進行Minor Gc時,其它內存代中的所有對象都應該被看做是根對象(儘管某些對象可能真的已經是垃圾對象了).本文所要講的根對象指的是在對內存堆或內存代進行Minor/Full Gc時都認同的“根對象”,這些跟對象主要來源於:Universe相關對象,Java/本地方法棧中的對象等等.

1. Universe相關對象

    Universe模塊在JVM中主要負責內存堆的管理這一塊,核心就是內存堆管理器的初始化工作.Universe中的根對象主要和JVM內部的Java對象表示模型相關:Klass框架和Oop框架.Klass對象表示的是Java類型的描述信息,Oop對象則是Java實例對象在JVM內存的存在形式.下面簡要的介紹常用的幾個Klass或Oop對象的內存佈局

Klass對象佈局


instanceKlass的內存佈局

                                                                          

普通java對象的內存佈局                                                                                     數組對象的內存佈局


   java對象的內存佈局中,_metadata表示的是該類型的描述信息對象指針,_mark表示的是該對象運行時狀態信息.

1.1 基本類型對應的java.lang.Class對象

oop Universe::_int_mirror                             = NULL;
oop Universe::_float_mirror                           = NULL;
oop Universe::_double_mirror                          = NULL;
oop Universe::_byte_mirror                            = NULL;
oop Universe::_bool_mirror                            = NULL;
oop Universe::_char_mirror                            = NULL;
oop Universe::_long_mirror                            = NULL;
oop Universe::_short_mirror                           = NULL;
oop Universe::_void_mirror                            = NULL;
oop Universe::_mirrors[T_VOID+1]                      = { NULL /*, NULL...*/ };

1.2 基本類型對應的數組類型的描述信息對象

klassOop Universe::_boolArrayKlassObj                 = NULL;
klassOop Universe::_byteArrayKlassObj                 = NULL;
klassOop Universe::_charArrayKlassObj                 = NULL;
klassOop Universe::_intArrayKlassObj                  = NULL;
klassOop Universe::_shortArrayKlassObj                = NULL;
klassOop Universe::_longArrayKlassObj                 = NULL;
klassOop Universe::_singleArrayKlassObj               = NULL;
klassOop Universe::_doubleArrayKlassObj               = NULL;
klassOop Universe::_typeArrayKlassObjs[T_VOID+1]      = { NULL /*, NULL...*/ };

1.3 Java類型相關的描述信息對象

klassOop Universe::_objectArrayKlassObj               = NULL;
klassOop Universe::_methodKlassObj                    = NULL;
klassOop Universe::_constMethodKlassObj               = NULL;
klassOop Universe::_methodDataKlassObj                = NULL;
klassOop Universe::_klassKlassObj                     = NULL;
klassOop Universe::_arrayKlassKlassObj                = NULL;
klassOop Universe::_objArrayKlassKlassObj             = NULL;
klassOop Universe::_typeArrayKlassKlassObj            = NULL;
klassOop Universe::_instanceKlassKlassObj             = NULL;
klassOop Universe::_constantPoolKlassObj              = NULL;
klassOop Universe::_constantPoolCacheKlassObj         = NULL;
klassOop Universe::_compiledICHolderKlassObj          = NULL;
klassOop Universe::_systemObjArrayKlassObj            = NULL;

1.4 常用Java方法描述信息對象

   這裏包含了JVM內部頻繁使用的3個java方法:java.lang.ref.Finalizer.register()/java.lang.ClassLoader.addClass()/java.lang.reflect.Method.invoke(),先簡單的介紹一下這三個java方法的主要用途.

LatestMethodOopCache* Universe::_finalizer_register_cache = NULL;
LatestMethodOopCache* Universe::_loader_addClass_cache    = NULL;
ActiveMethodOopsCache* Universe::_reflect_invoke_cache    = NULL;

    在java.lang.Object裏面有一個finalize()的空方法,一旦某個類型實現了這個方法,就會觸發JVM的內部行爲:該類型對應的實例對象在被Gc之後,JVM就會調用它的finalize(),但並不保證一定會調用.

/**
 * 註冊一個實現了finalize()的對象,當gc回收該對象時會掉用它的finalize()
 */
instanceOop instanceKlass::register_finalizer(instanceOop i, TRAPS) {
  if (TraceFinalizerRegistration) {
    tty->print("Registered ");
    i->print_value_on(tty);
    tty->print_cr(" (" INTPTR_FORMAT ") as finalizable", (address)i);
  }
  
  /**
   * 構造調用Java方法所需的參數
   */
  
  instanceHandle h_i(THREAD, i);
  // Pass the handle as argument, JavaCalls::call expects oop as jobjects
  JavaValue result(T_VOID);
  JavaCallArguments args(h_i);
  methodHandle mh (THREAD, Universe::finalizer_register_method());
  
  //調用java.lang.ref.Finalizer.register()方法
  JavaCalls::call(&result, mh, &args, CHECK_NULL);
  return h_i();
}
   java.lang.ClassLoader.addClass()主要用於在jni中加載類的調用.

1.5 內存分配相關的異常實例對象

oop Universe::_out_of_memory_error_java_heap          = NULL;
oop Universe::_out_of_memory_error_perm_gen           = NULL;
oop Universe::_out_of_memory_error_array_size         = NULL;
oop Universe::_out_of_memory_error_gc_overhead_limit  = NULL;
objArrayOop Universe::_preallocated_out_of_memory_error_array = NULL;
oop Universe::_null_ptr_exception_instance            = NULL;
oop Universe::_arithmetic_exception_instance          = NULL;
oop Universe::_virtual_machine_error_instance         = NULL;
oop Universe::_vm_exception                           = NULL;

2. 本地方法創建的全局對象

    JNIHandles中保存了所有Java線程在執行本地方法時申請的全局對象句柄:

JNIHandleBlock* JNIHandles::_global_handles       = NULL;


全局本地對象句柄的視圖

/**
 * 爲指定的實例對象分配一個句柄
 */
jobject JNIHandleBlock::allocate_handle(oop obj) {
  assert(Universe::heap()->is_in_reserved(obj), "sanity check");

  if (_top == 0) {
    // This is the first allocation or the initial block got zapped when
    // entering a native function. If we have any following blocks they are
    // not valid anymore.
    for (JNIHandleBlock* current = _next; current != NULL;
         current = current->_next) {
      assert(current->_last == NULL, "only first block should have _last set");
      assert(current->_free_list == NULL,
             "only first block should have _free_list set");
      current->_top = 0;
      if (ZapJNIHandleArea) current->zap();
    }

    // Clear initial block
    _free_list = NULL;
    _allocate_before_rebuild = 0;
    _last = this;
    if (ZapJNIHandleArea) zap();
  }

  //最後一個塊有空閒句柄
  if (_last->_top < block_size_in_oops) {
    oop* handle = &(_last->_handles)[_last->_top++];
    *handle = obj;
    return (jobject) handle;
  }

  // Try free list
  if (_free_list != NULL) {
    oop* handle = _free_list;
    _free_list = (oop*) *_free_list;
    *handle = obj;
    return (jobject) handle;
  }

  // Check if unused block follow last
  if (_last->_next != NULL) {
    // update last and retry
    _last = _last->_next;
    return allocate_handle(obj);
  }

  // No space available, we have to rebuild free list or expand
  if (_allocate_before_rebuild == 0) {
      rebuild_free_list();        // updates _allocate_before_rebuild counter

  } else {	//分配一個新的句柄存儲塊

    // Append new block
    Thread* thread = Thread::current();
    Handle obj_handle(thread, obj);

    // This can block, so we need to preserve obj accross call.
    _last->_next = JNIHandleBlock::allocate_block(thread);
    _last = _last->_next;
    _allocate_before_rebuild--;
    obj = obj_handle();
  }

  return allocate_handle(obj);  // retry
}

void JNIHandleBlock::oops_do(OopClosure* f) {
  JNIHandleBlock* current_chain = this;
  // Iterate over chain of blocks, followed by chains linked through the
  // pop frame links.
  while (current_chain != NULL) {
    for (JNIHandleBlock* current = current_chain; current != NULL;
         current = current->_next) {
      assert(current == current_chain || current->pop_frame_link() == NULL,
        "only blocks first in chain should have pop frame link set");
      for (int index = 0; index < current->_top; index++) {
        oop* root = &(current->_handles)[index];
        oop value = *root;
        // traverse heap pointers only, not deleted handles or free list
        // pointers
        if (value != NULL && Universe::heap()->is_in_reserved(value)) {
          f->do_oop(root);
        }
      }
      // the next handle block is valid only if current block is full
      if (current->_top < block_size_in_oops) {
        break;
      }
    }
    current_chain = current_chain->pop_frame_link();

  }//while

}

/**
 * 遍歷所有的全局本地對象
 */
void JNIHandles::oops_do(OopClosure* f) {
  f->do_oop(&_deleted_handle);
  _global_handles->oops_do(f);
}

3. 方法棧中的對象

    這裏主要指的是每一個Java線程在執行過程中創建的私有局部變量,分爲兩塊:一是在執行Java方法時創建的,二是在執行本地方法時創建的.

void Threads::oops_do(OopClosure* f, CodeBlobClosure* cf) {
  ALL_JAVA_THREADS(p) {
    p->oops_do(f, cf);
  }

  VMThread::vm_thread()->oops_do(f, cf);
}

void Thread::oops_do(OopClosure* f, CodeBlobClosure* cf) {

  active_handles()->oops_do(f);	//本地局部對象

  // Do oop for ThreadShadow
  f->do_oop((oop*)&_pending_exception);	//當前線程可能已經拋出的異常對象

  handle_area()->oops_do(f);	//局部對象
}

1.Java方法棧中的對象

  每一個Java線程都私有一個句柄區_handle_area來存儲其運行過程中創建的臨時對象,這個句柄區是隨着Java線程的棧幀變化的,這個句柄區的數據結構如下


    Java線程每調用一個Java方法就會創建一個對應HandleMark來保存此時Java線程分配對象句柄的上下文環境,然後等調用返回後即行恢復:

void JavaCalls::call_helper(JavaValue* result, methodHandle* m, JavaCallArguments* args, TRAPS) {
     ...

     {
      HandleMark hm(thread);  // HandleMark used by HandleMarkCleaner

      printf("%s[%d] [tid: %lu]: 開始執行方法[%s]...\n", __FILE__, __LINE__, pthread_self(), method->name()->as_C_string());
      StubRoutines::call_stub()(
        (address)&link,
        // (intptr_t*)&(result->_value), // see NOTE above (compiler problem)
        result_val_address,          // see NOTE above (compiler problem)
        result_type,
        method(),
        entry_point,
        args->parameters(),
        args->size_of_parameters(),
        CHECK
      );

      result = link.result();  // circumvent MS C++ 5.0 compiler bug (result is clobbered across call)
      // Preserve oop return value across possible gc points
      if (oop_result_flag) {
        thread->set_vm_result((oop) result->get_jobject());
      }

    }

    ...
}

void HandleMark::initialize(Thread* thread) {
  _thread = thread;

  //保存當前線程分配對象句柄的地址
  _area  = thread->handle_area();
  // Save current top
  _chunk = _area->_chunk;
  _hwm   = _area->_hwm;
  _max   = _area->_max;

  NOT_PRODUCT(_size_in_bytes = _area->_size_in_bytes;)
  debug_only(_area->_handle_mark_nesting++);
  assert(_area->_handle_mark_nesting > 0, "must stack allocate HandleMarks");
  debug_only(Atomic::inc(&_nof_handlemarks);)

  // Link this in the thread
  set_previous_handle_mark(thread->last_handle_mark());
  thread->set_last_handle_mark(this);
}


HandleMark::~HandleMark() {
  HandleArea* area = _area;   // help compilers with poor alias analysis

  assert(area == _thread->handle_area(), "sanity check");
  assert(area->_handle_mark_nesting > 0, "must stack allocate HandleMarks" );
  debug_only(area->_handle_mark_nesting--);

  // Debug code to trace the number of handles allocated per mark/
#ifdef ASSERT
  if (TraceHandleAllocation) {
    size_t handles = 0;
    Chunk *c = _chunk->next();
    if (c == NULL) {
      handles = area->_hwm - _hwm; // no new chunk allocated
    } else {
      handles = _max - _hwm;      // add rest in first chunk
      while(c != NULL) {
        handles += c->length();
        c = c->next();
      }
      handles -= area->_max - area->_hwm; // adjust for last trunk not full
    }
    handles /= sizeof(void *); // Adjust for size of a handle
    if (handles > HandleAllocationLimit) {
      // Note: _nof_handlemarks is only set in debug mode
      warning("%d: Allocated in HandleMark : %d", _nof_handlemarks, handles);
    }
  }
#endif

  // Delete later chunks
  if( _chunk->next() ) {
    _chunk->next_chop();
  }
  
  // 恢復之前線程分配對象句柄的地址
  area->_chunk = _chunk;
  area->_hwm = _hwm;
  area->_max = _max;
  NOT_PRODUCT(area->set_size_in_bytes(_size_in_bytes);)

#ifdef ASSERT
  // clear out first chunk (to detect allocation bugs)
  if (ZapVMHandleArea) {
    memset(_hwm, badHandleValue, _max - _hwm);
  }
  Atomic::dec(&_nof_handlemarks);
#endif

  // Unlink this from the thread
  _thread->set_last_handle_mark(previous_handle_mark());
}


2.本地方法棧中的對象

    Java線程使用一個對象句柄存儲塊JNIHandleBlock來爲其於在本地方法中申請的臨時對象創建對應的句柄,JNIHandleBlock中有一個_pop_frame_link屬性,被用來保存Java線程切換方法時分配本地對象句柄的上下文環境.


4. 對象同步監視器中的對象

  對於每一個被synchronized的Java對象,JVM會在內部爲其創建一個對應的對象監視器ObjectMonitor,用來控制與其相關的線程:

void ObjectSynchronizer::oops_do(OopClosure* f) {
  assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint");
  for (ObjectMonitor* block = gBlockList; block != NULL; block = next(block)) {
    assert(block->object() == CHAINMARKER, "must be a block header");
    for (int i = 1; i < _BLOCKSIZE; i++) {
      ObjectMonitor* mid = &block[i];
      if (mid->object() != NULL) {
        f->do_oop((oop*)mid->object_addr());
      }
    }
  }
}

5. Java級管理接口(Management)中的對象

    Management中的根對象主要是關於JVM中的線程/內存/Gc的統計,監控等相關的類型描述信息或對象:

void Management::oops_do(OopClosure* f) {
  MemoryService::oops_do(f);
  ThreadService::oops_do(f);

  f->do_oop((oop*) &_sensor_klass);
  f->do_oop((oop*) &_threadInfo_klass);
  f->do_oop((oop*) &_memoryUsage_klass);
  f->do_oop((oop*) &_memoryPoolMXBean_klass);
  f->do_oop((oop*) &_memoryManagerMXBean_klass);
  f->do_oop((oop*) &_garbageCollectorMXBean_klass);
  f->do_oop((oop*) &_managementFactory_klass);
  f->do_oop((oop*) &_garbageCollectorImpl_klass);
  f->do_oop((oop*) &_gcInfo_klass);
}

6. 虛擬機工具接口(JVMTI)中的對象

     關於Java虛擬機工具接口中的根對象,本文不詳細介紹

7. 系統字典(SystemDictionary)中的對象

    SystemDictionary中的根對象主要包括各種基礎類型的描述信息:

java.lang.Object
java.lang.String
java.lang.Class
java.lang.Cloneable
java.lang.ClassLoader
java.io.Serializable
java.lang.System
java.lang.Throwable
java.lang.Error
java.lang.ThreadDeath
java.lang.Exception
java.lang.RuntimeException
java.security.ProtectionDomain
java.security.AccessControlContext
java.lang.ClassNotFoundException
java.lang.NoClassDefFoundError
java.lang.LinkageError
java.lang.ClassCastException
java.lang.ArrayStoreException
java.lang.VirtualMachineError
java.lang.OutOfMemoryError
java.lang.StackOverflowError
java.lang.IllegalMonitorStateException

java.lang.ref.Reference   
java.lang.ref.SoftReference
java.lang.ref.WeakReference
java.lang.ref.FinalReference
java.lang.ref.PhantomReference 

java.lang.ref.Finalizer
java.lang.Thread  
java.lang.ThreadGroup
java.util.Properties 
java.lang.reflect.AccessibleObject
java.lang.reflect.Field
java.lang.reflect.Method 
java.lang.reflect.Constructor

sun.reflect.MagicAccessorImpl
sun.reflect.MethodAccessorImpl
sun.reflect.ConstructorAccessorImpl  
sun.reflect.DelegatingClassLoader
sun.reflect.ConstantPool
sun.reflect.UnsafeStaticFieldAccessorImpl
java.lang.invoke.MethodHandle  
java.lang.invoke.MemberName          
java.lang.invoke.MethodHandleNatives  
java.lang.invoke.AdapterMethodHandle   
java.lang.invoke.BoundMethodHandle   
java.lang.invoke.DirectMethodHandle    
java.lang.invoke.MethodType            
java.lang.invoke.MethodTypeForm     
java.lang.BootstrapMethodError      
java.lang.invoke.WrongMethodTypeException
java.lang.invoke.CallSite          
java.lang.invoke.CountingMethodHandle
java.lang.invoke.ConstantCallSite     
java.lang.invoke.MutableCallSite  
java.lang.invoke.VolatileCallSite

java.lang.StringBuffer 
java.lang.StringBuilder  
java.nio.Buffer
 
sun.misc.AtomicLongCSImpl
sun.jkernel.DownloadManager
sun.misc.PostVMInitHook

java.lang.Boolean    
java.lang.Character 
java.lang.Float       
java.lang.Double 
java.lang.Byte        
java.lang.Short    
java.lang.Integer
java.lang.Long 

8. 字符串常量表(StringTable)中的對象

   顧名思義,字符串常量表中存儲的主要是常量字符串,特別是類型的全限定名,方法的簽名等對應的字符串.

9. 代碼高速緩存區中的對象

    代碼高速緩存區CodeCache中主要存儲的Java方法被本地編譯之後對應的代碼片段.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章