我是靠谱客的博主 威武向日葵,最近开发中收集的这篇文章主要介绍内存堆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对象

[cpp]   view plain copy 在CODE上查看代码片 派生到我的代码片
  1. oop Universe::_int_mirror                             = NULL;  
  2. oop Universe::_float_mirror                           = NULL;  
  3. oop Universe::_double_mirror                          = NULL;  
  4. oop Universe::_byte_mirror                            = NULL;  
  5. oop Universe::_bool_mirror                            = NULL;  
  6. oop Universe::_char_mirror                            = NULL;  
  7. oop Universe::_long_mirror                            = NULL;  
  8. oop Universe::_short_mirror                           = NULL;  
  9. oop Universe::_void_mirror                            = NULL;  
  10. oop Universe::_mirrors[T_VOID+1]                      = { NULL /*, NULL...*/ };  

1.2 基本类型对应的数组类型的描述信息对象

[cpp]   view plain copy 在CODE上查看代码片 派生到我的代码片
  1. klassOop Universe::_boolArrayKlassObj                 = NULL;  
  2. klassOop Universe::_byteArrayKlassObj                 = NULL;  
  3. klassOop Universe::_charArrayKlassObj                 = NULL;  
  4. klassOop Universe::_intArrayKlassObj                  = NULL;  
  5. klassOop Universe::_shortArrayKlassObj                = NULL;  
  6. klassOop Universe::_longArrayKlassObj                 = NULL;  
  7. klassOop Universe::_singleArrayKlassObj               = NULL;  
  8. klassOop Universe::_doubleArrayKlassObj               = NULL;  
  9. klassOop Universe::_typeArrayKlassObjs[T_VOID+1]      = { NULL /*, NULL...*/ };  

1.3 Java类型相关的描述信息对象

[cpp]   view plain copy 在CODE上查看代码片 派生到我的代码片
  1. klassOop Universe::_objectArrayKlassObj               = NULL;  
  2. klassOop Universe::_methodKlassObj                    = NULL;  
  3. klassOop Universe::_constMethodKlassObj               = NULL;  
  4. klassOop Universe::_methodDataKlassObj                = NULL;  
  5. klassOop Universe::_klassKlassObj                     = NULL;  
  6. klassOop Universe::_arrayKlassKlassObj                = NULL;  
  7. klassOop Universe::_objArrayKlassKlassObj             = NULL;  
  8. klassOop Universe::_typeArrayKlassKlassObj            = NULL;  
  9. klassOop Universe::_instanceKlassKlassObj             = NULL;  
  10. klassOop Universe::_constantPoolKlassObj              = NULL;  
  11. klassOop Universe::_constantPoolCacheKlassObj         = NULL;  
  12. klassOop Universe::_compiledICHolderKlassObj          = NULL;  
  13. 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方法的主要用途.

[cpp]   view plain copy 在CODE上查看代码片 派生到我的代码片
  1. LatestMethodOopCache* Universe::_finalizer_register_cache = NULL;  
  2. LatestMethodOopCache* Universe::_loader_addClass_cache    = NULL;  
  3. ActiveMethodOopsCache* Universe::_reflect_invoke_cache    = NULL;  

    在java.lang.Object里面有一个finalize()的空方法,一旦某个类型实现了这个方法,就会触发JVM的内部行为:该类型对应的实例对象在被Gc之后,JVM就会调用它的finalize(),但并不保证一定会调用.

[cpp]   view plain copy 在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.  * 注册一个实现了finalize()的对象,当gc回收该对象时会掉用它的finalize() 
  3.  */  
  4. instanceOop instanceKlass::register_finalizer(instanceOop i, TRAPS) {  
  5.   if (TraceFinalizerRegistration) {  
  6.     tty->print("Registered ");  
  7.     i->print_value_on(tty);  
  8.     tty->print_cr(" (" INTPTR_FORMAT ") as finalizable", (address)i);  
  9.   }  
  10.     
  11.   /** 
  12.    * 构造调用Java方法所需的参数 
  13.    */  
  14.     
  15.   instanceHandle h_i(THREAD, i);  
  16.   // Pass the handle as argument, JavaCalls::call expects oop as jobjects  
  17.   JavaValue result(T_VOID);  
  18.   JavaCallArguments args(h_i);  
  19.   methodHandle mh (THREAD, Universe::finalizer_register_method());  
  20.     
  21.   //调用java.lang.ref.Finalizer.register()方法  
  22.   JavaCalls::call(&result, mh, &args, CHECK_NULL);  
  23.   return h_i();  
  24. }  
   java.lang.ClassLoader.addClass()主要用于在jni中加载类的调用.

1.5 内存分配相关的异常实例对象

[cpp]   view plain copy 在CODE上查看代码片 派生到我的代码片
  1. oop Universe::_out_of_memory_error_java_heap          = NULL;  
  2. oop Universe::_out_of_memory_error_perm_gen           = NULL;  
  3. oop Universe::_out_of_memory_error_array_size         = NULL;  
  4. oop Universe::_out_of_memory_error_gc_overhead_limit  = NULL;  
  5. objArrayOop Universe::_preallocated_out_of_memory_error_array = NULL;  
  6. oop Universe::_null_ptr_exception_instance            = NULL;  
  7. oop Universe::_arithmetic_exception_instance          = NULL;  
  8. oop Universe::_virtual_machine_error_instance         = NULL;  
  9. oop Universe::_vm_exception                           = NULL;  

2. 本地方法创建的全局对象

    JNIHandles中保存了所有Java线程在执行本地方法时申请的全局对象句柄:

[cpp]   view plain copy 在CODE上查看代码片 派生到我的代码片
  1. JNIHandleBlock* JNIHandles::_global_handles       = NULL;  


全局本地对象句柄的视图

[cpp]   view plain copy 在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.  * 为指定的实例对象分配一个句柄 
  3.  */  
  4. jobject JNIHandleBlock::allocate_handle(oop obj) {  
  5.   assert(Universe::heap()->is_in_reserved(obj), "sanity check");  
  6.   
  7.   if (_top == 0) {  
  8.     // This is the first allocation or the initial block got zapped when  
  9.     // entering a native function. If we have any following blocks they are  
  10.     // not valid anymore.  
  11.     for (JNIHandleBlock* current = _next; current != NULL;  
  12.          current = current->_next) {  
  13.       assert(current->_last == NULL, "only first block should have _last set");  
  14.       assert(current->_free_list == NULL,  
  15.              "only first block should have _free_list set");  
  16.       current->_top = 0;  
  17.       if (ZapJNIHandleArea) current->zap();  
  18.     }  
  19.   
  20.     // Clear initial block  
  21.     _free_list = NULL;  
  22.     _allocate_before_rebuild = 0;  
  23.     _last = this;  
  24.     if (ZapJNIHandleArea) zap();  
  25.   }  
  26.   
  27.   //最后一个块有空闲句柄  
  28.   if (_last->_top < block_size_in_oops) {  
  29.     oop* handle = &(_last->_handles)[_last->_top++];  
  30.     *handle = obj;  
  31.     return (jobject) handle;  
  32.   }  
  33.   
  34.   // Try free list  
  35.   if (_free_list != NULL) {  
  36.     oop* handle = _free_list;  
  37.     _free_list = (oop*) *_free_list;  
  38.     *handle = obj;  
  39.     return (jobject) handle;  
  40.   }  
  41.   
  42.   // Check if unused block follow last  
  43.   if (_last->_next != NULL) {  
  44.     // update last and retry  
  45.     _last = _last->_next;  
  46.     return allocate_handle(obj);  
  47.   }  
  48.   
  49.   // No space available, we have to rebuild free list or expand  
  50.   if (_allocate_before_rebuild == 0) {  
  51.       rebuild_free_list();        // updates _allocate_before_rebuild counter  
  52.   
  53.   } else {  //分配一个新的句柄存储块  
  54.   
  55.     // Append new block  
  56.     Thread* thread = Thread::current();  
  57.     Handle obj_handle(thread, obj);  
  58.   
  59.     // This can block, so we need to preserve obj accross call.  
  60.     _last->_next = JNIHandleBlock::allocate_block(thread);  
  61.     _last = _last->_next;  
  62.     _allocate_before_rebuild--;  
  63.     obj = obj_handle();  
  64.   }  
  65.   
  66.   return allocate_handle(obj);  // retry  
  67. }  
  68.   
  69. void JNIHandleBlock::oops_do(OopClosure* f) {  
  70.   JNIHandleBlock* current_chain = this;  
  71.   // Iterate over chain of blocks, followed by chains linked through the  
  72.   // pop frame links.  
  73.   while (current_chain != NULL) {  
  74.     for (JNIHandleBlock* current = current_chain; current != NULL;  
  75.          current = current->_next) {  
  76.       assert(current == current_chain || current->pop_frame_link() == NULL,  
  77.         "only blocks first in chain should have pop frame link set");  
  78.       for (int index = 0; index < current->_top; index++) {  
  79.         oop* root = &(current->_handles)[index];  
  80.         oop value = *root;  
  81.         // traverse heap pointers only, not deleted handles or free list  
  82.         // pointers  
  83.         if (value != NULL && Universe::heap()->is_in_reserved(value)) {  
  84.           f->do_oop(root);  
  85.         }  
  86.       }  
  87.       // the next handle block is valid only if current block is full  
  88.       if (current->_top < block_size_in_oops) {  
  89.         break;  
  90.       }  
  91.     }  
  92.     current_chain = current_chain->pop_frame_link();  
  93.   
  94.   }//while  
  95.   
  96. }  
  97.   
  98. /** 
  99.  * 遍历所有的全局本地对象 
  100.  */  
  101. void JNIHandles::oops_do(OopClosure* f) {  
  102.   f->do_oop(&_deleted_handle);  
  103.   _global_handles->oops_do(f);  
  104. }  

3. 方法栈中的对象

    这里主要指的是每一个Java线程在执行过程中创建的私有局部变量,分为两块:一是在执行Java方法时创建的,二是在执行本地方法时创建的. 

[cpp]   view plain copy 在CODE上查看代码片 派生到我的代码片
  1. void Threads::oops_do(OopClosure* f, CodeBlobClosure* cf) {  
  2.   ALL_JAVA_THREADS(p) {  
  3.     p->oops_do(f, cf);  
  4.   }  
  5.   
  6.   VMThread::vm_thread()->oops_do(f, cf);  
  7. }  
  8.   
  9. void Thread::oops_do(OopClosure* f, CodeBlobClosure* cf) {  
  10.   
  11.   active_handles()->oops_do(f);  //本地局部对象  
  12.   
  13.   // Do oop for ThreadShadow  
  14.   f->do_oop((oop*)&_pending_exception);  //当前线程可能已经抛出的异常对象  
  15.   
  16.   handle_area()->oops_do(f); //局部对象  
  17. }  

1.Java方法栈中的对象

  每一个Java线程都私有一个句柄区_handle_area来存储其运行过程中创建的临时对象,这个句柄区是随着Java线程的栈帧变化的,这个句柄区的数据结构如下


    Java线程每调用一个Java方法就会创建一个对应HandleMark来保存此时Java线程分配对象句柄的上下文环境,然后等调用返回后即行恢复:

[cpp]   view plain copy 在CODE上查看代码片 派生到我的代码片
  1. void JavaCalls::call_helper(JavaValue* result, methodHandle* m, JavaCallArguments* args, TRAPS) {  
  2.      ...  
  3.   
  4.      {  
  5.       HandleMark hm(thread);  // HandleMark used by HandleMarkCleaner  
  6.   
  7.       printf("%s[%d] [tid: %lu]: 开始执行方法[%s]...n", __FILE__, __LINE__, pthread_self(), method->name()->as_C_string());  
  8.       StubRoutines::call_stub()(  
  9.         (address)&link,  
  10.         // (intptr_t*)&(result->_value), // see NOTE above (compiler problem)  
  11.         result_val_address,          // see NOTE above (compiler problem)  
  12.         result_type,  
  13.         method(),  
  14.         entry_point,  
  15.         args->parameters(),  
  16.         args->size_of_parameters(),  
  17.         CHECK  
  18.       );  
  19.   
  20.       result = link.result();  // circumvent MS C++ 5.0 compiler bug (result is clobbered across call)  
  21.       // Preserve oop return value across possible gc points  
  22.       if (oop_result_flag) {  
  23.         thread->set_vm_result((oop) result->get_jobject());  
  24.       }  
  25.   
  26.     }  
  27.   
  28.     ...  
  29. }  
  30.   
  31. void HandleMark::initialize(Thread* thread) {  
  32.   _thread = thread;  
  33.   
  34.   //保存当前线程分配对象句柄的地址  
  35.   _area  = thread->handle_area();  
  36.   // Save current top  
  37.   _chunk = _area->_chunk;  
  38.   _hwm   = _area->_hwm;  
  39.   _max   = _area->_max;  
  40.   
  41.   NOT_PRODUCT(_size_in_bytes = _area->_size_in_bytes;)  
  42.   debug_only(_area->_handle_mark_nesting++);  
  43.   assert(_area->_handle_mark_nesting > 0, "must stack allocate HandleMarks");  
  44.   debug_only(Atomic::inc(&_nof_handlemarks);)  
  45.   
  46.   // Link this in the thread  
  47.   set_previous_handle_mark(thread->last_handle_mark());  
  48.   thread->set_last_handle_mark(this);  
  49. }  
  50.   
  51.   
  52. HandleMark::~HandleMark() {  
  53.   HandleArea* area = _area;   // help compilers with poor alias analysis  
  54.   
  55.   assert(area == _thread->handle_area(), "sanity check");  
  56.   assert(area->_handle_mark_nesting > 0, "must stack allocate HandleMarks" );  
  57.   debug_only(area->_handle_mark_nesting--);  
  58.   
  59.   // Debug code to trace the number of handles allocated per mark/  
  60. #ifdef ASSERT  
  61.   if (TraceHandleAllocation) {  
  62.     size_t handles = 0;  
  63.     Chunk *c = _chunk->next();  
  64.     if (c == NULL) {  
  65.       handles = area->_hwm - _hwm; // no new chunk allocated  
  66.     } else {  
  67.       handles = _max - _hwm;      // add rest in first chunk  
  68.       while(c != NULL) {  
  69.         handles += c->length();  
  70.         c = c->next();  
  71.       }  
  72.       handles -= area->_max - area->_hwm; // adjust for last trunk not full  
  73.     }  
  74.     handles /= sizeof(void *); // Adjust for size of a handle  
  75.     if (handles > HandleAllocationLimit) {  
  76.       // Note: _nof_handlemarks is only set in debug mode  
  77.       warning("%d: Allocated in HandleMark : %d", _nof_handlemarks, handles);  
  78.     }  
  79.   }  
  80. #endif  
  81.   
  82.   // Delete later chunks  
  83.   if( _chunk->next() ) {  
  84.     _chunk->next_chop();  
  85.   }  
  86.     
  87.   // 恢复之前线程分配对象句柄的地址  
  88.   area->_chunk = _chunk;  
  89.   area->_hwm = _hwm;  
  90.   area->_max = _max;  
  91.   NOT_PRODUCT(area->set_size_in_bytes(_size_in_bytes);)  
  92.   
  93. #ifdef ASSERT  
  94.   // clear out first chunk (to detect allocation bugs)  
  95.   if (ZapVMHandleArea) {  
  96.     memset(_hwm, badHandleValue, _max - _hwm);  
  97.   }  
  98.   Atomic::dec(&_nof_handlemarks);  
  99. #endif  
  100.   
  101.   // Unlink this from the thread  
  102.   _thread->set_last_handle_mark(previous_handle_mark());  
  103. }  


2.本地方法栈中的对象

    Java线程使用一个对象句柄存储块JNIHandleBlock来为其于在本地方法中申请的临时对象创建对应的句柄,JNIHandleBlock中有一个_pop_frame_link属性,被用来保存Java线程切换方法时分配本地对象句柄的上下文环境.


4. 对象同步监视器中的对象

  对于每一个被synchronized的Java对象,JVM会在内部为其创建一个对应的对象监视器ObjectMonitor,用来控制与其相关的线程:

[cpp]   view plain copy 在CODE上查看代码片 派生到我的代码片
  1. void ObjectSynchronizer::oops_do(OopClosure* f) {  
  2.   assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint");  
  3.   for (ObjectMonitor* block = gBlockList; block != NULL; block = next(block)) {  
  4.     assert(block->object() == CHAINMARKER, "must be a block header");  
  5.     for (int i = 1; i < _BLOCKSIZE; i++) {  
  6.       ObjectMonitor* mid = &block[i];  
  7.       if (mid->object() != NULL) {  
  8.         f->do_oop((oop*)mid->object_addr());  
  9.       }  
  10.     }  
  11.   }  
  12. }  

5. Java级管理接口(Management)中的对象

    Management中的根对象主要是关于JVM中的线程/内存/Gc的统计,监控等相关的类型描述信息或对象:

[cpp]   view plain copy 在CODE上查看代码片 派生到我的代码片
  1. void Management::oops_do(OopClosure* f) {  
  2.   MemoryService::oops_do(f);  
  3.   ThreadService::oops_do(f);  
  4.   
  5.   f->do_oop((oop*) &_sensor_klass);  
  6.   f->do_oop((oop*) &_threadInfo_klass);  
  7.   f->do_oop((oop*) &_memoryUsage_klass);  
  8.   f->do_oop((oop*) &_memoryPoolMXBean_klass);  
  9.   f->do_oop((oop*) &_memoryManagerMXBean_klass);  
  10.   f->do_oop((oop*) &_garbageCollectorMXBean_klass);  
  11.   f->do_oop((oop*) &_managementFactory_klass);  
  12.   f->do_oop((oop*) &_garbageCollectorImpl_klass);  
  13.   f->do_oop((oop*) &_gcInfo_klass);  
  14. }  

6. 虚拟机工具接口(JVMTI)中的对象

     关于Java虚拟机工具接口中的根对象,本文不详细介绍

7. 系统字典(SystemDictionary)中的对象

    SystemDictionary中的根对象主要包括各种基础类型的描述信息:

[cpp]   view plain copy 在CODE上查看代码片 派生到我的代码片
  1. java.lang.Object  
  2. java.lang.String  
  3. java.lang.Class  
  4. java.lang.Cloneable  
  5. java.lang.ClassLoader  
  6. java.io.Serializable  
  7. java.lang.System  
  8. java.lang.Throwable  
  9. java.lang.Error  
  10. java.lang.ThreadDeath  
  11. java.lang.Exception  
  12. java.lang.RuntimeException  
  13. java.security.ProtectionDomain  
  14. java.security.AccessControlContext  
  15. java.lang.ClassNotFoundException  
  16. java.lang.NoClassDefFoundError  
  17. java.lang.LinkageError  
  18. java.lang.ClassCastException  
  19. java.lang.ArrayStoreException  
  20. java.lang.VirtualMachineError  
  21. java.lang.OutOfMemoryError  
  22. java.lang.StackOverflowError  
  23. java.lang.IllegalMonitorStateException  
  24.   
  25. java.lang.ref.Reference     
  26. java.lang.ref.SoftReference  
  27. java.lang.ref.WeakReference  
  28. java.lang.ref.FinalReference  
  29. java.lang.ref.PhantomReference   
  30.   
  31. java.lang.ref.Finalizer  
  32. java.lang.Thread    
  33. java.lang.ThreadGroup  
  34. java.util.Properties   
  35. java.lang.reflect.AccessibleObject  
  36. java.lang.reflect.Field  
  37. java.lang.reflect.Method   
  38. java.lang.reflect.Constructor  
  39.   
  40. sun.reflect.MagicAccessorImpl  
  41. sun.reflect.MethodAccessorImpl  
  42. sun.reflect.ConstructorAccessorImpl    
  43. sun.reflect.DelegatingClassLoader  
  44. sun.reflect.ConstantPool  
  45. sun.reflect.UnsafeStaticFieldAccessorImpl  
  46. java.lang.invoke.MethodHandle    
  47. java.lang.invoke.MemberName            
  48. java.lang.invoke.MethodHandleNatives    
  49. java.lang.invoke.AdapterMethodHandle     
  50. java.lang.invoke.BoundMethodHandle     
  51. java.lang.invoke.DirectMethodHandle      
  52. java.lang.invoke.MethodType              
  53. java.lang.invoke.MethodTypeForm       
  54. java.lang.BootstrapMethodError        
  55. java.lang.invoke.WrongMethodTypeException  
  56. java.lang.invoke.CallSite            
  57. java.lang.invoke.CountingMethodHandle  
  58. java.lang.invoke.ConstantCallSite       
  59. java.lang.invoke.MutableCallSite    
  60. java.lang.invoke.VolatileCallSite  
  61.   
  62. java.lang.StringBuffer   
  63. java.lang.StringBuilder    
  64. java.nio.Buffer  
  65.    
  66. sun.misc.AtomicLongCSImpl  
  67. sun.jkernel.DownloadManager  
  68. sun.misc.PostVMInitHook  
  69.   
  70. java.lang.Boolean      
  71. java.lang.Character   
  72. java.lang.Float         
  73. java.lang.Double   
  74. java.lang.Byte          
  75. java.lang.Short      
  76. java.lang.Integer  
  77. java.lang.Long   

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

   顾名思义,字符串常量表中存储的主要是常量字符串,特别是类型的全限定名,方法的签名等对应的字符串.

9. 代码高速缓存区中的对象

    代码高速缓存区CodeCache中主要存储的Java方法被本地编译之后对应的代码片段.

最后

以上就是威武向日葵为你收集整理的内存堆Gc时公认的根对象的全部内容,希望文章能够帮你解决内存堆Gc时公认的根对象所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(73)

评论列表共有 0 条评论

立即
投稿
返回
顶部