概述
内存堆的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();
- }
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方法被本地编译之后对应的代码片段.
最后
以上就是威武向日葵为你收集整理的内存堆Gc时公认的根对象的全部内容,希望文章能够帮你解决内存堆Gc时公认的根对象所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复