[Inside HotSpot] Epsilon GC

Epislon GC源于RedHat开发者Aleksey Shipilëv提交的一份JEP 318: Epsilon: A No-Op Garbage Collector (Experimental)草案,该GC只做内存分配而不做内存回收(reclaim),当堆空间耗尽关闭JVM即可,因为它不做任何垃圾回收工作,所以又叫No-op GC。也因为它简单,很适合用来入门OpenJDK GC源码,看看一个最小化可行的垃圾回收器应该具备哪些功能。

Epislon GC源码位于gc/epsilon:

hotspot/share/gc/epsilon: epsilon_globals.hpp # GC提供的一些JVM参数,如-XX:+UseEpsilonGC epsilonArguments.cpp epsilonArguments.hpp # GC参数在JVM中的表示,是否使用TLAB等,是否开启EpsilonGC等 epsilonBarrierSet.cpp epsilonBarrierSet.hpp # GC barrier,用于线程创建的时候初始化TLAB epsilonCollectorPolicy.hpp # 垃圾回收策略,堆初始化大小,最小,最大值,对齐等信息 epsilonHeap.cpp # 包含堆初始化,内存分配,垃圾回收接口 epsilonHeap.hpp # 真正的堆表示,EpsilonGC独有 epsilonMemoryPool.cpp epsilonMemoryPool.hpp # 感知该堆内存的使用情况,gc次数,gc线程数,上次gc时间等 epsilonMonitoringSupport.cpp epsilonMonitoringSupport.hpp # perfdata支持 epsilonThreadLocalData.hpp # TLAB内存分配 vmStructs_epsilon.hpp # serviceability agent支持

另外为了启动EpislonGC需要添加JVM参数-XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC,为了输出GC日志查看详细过程添加JVM参数-Xlog:gc*=trace(仅限fastdebug版JVM)

2. EpsilonGC创建

虚拟机在创建早期会调用GCArguments::initialize()初始化GC参数,然后创建中期会配置好堆空间并调用GCArguments::create_heap()创建堆:

// hotspot\share\runtime\thread.cpp // 创建早期 jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) { ... jint ergo_result = Arguments::apply_ergo(); // GCArguments::initialize() } // hotspot\share\memory\universe.cpp // 创建中期 jint Universe::initialize_heap() { _collectedHeap = create_heap(); // GCArguments::create_heap() jint status = _collectedHeap->initialize(); ... }

首先我们创建GCArguments的子类用来表示EpsilonGC的GC参数:

class EpsilonArguments : public GCArguments { public: virtual void initialize(); virtual size_t conservative_max_heap_alignment(); virtual CollectedHeap* create_heap(); };

GCArguments::initialize()可以初始化GC参数,不过并没有什么可以初始化的,Epsilon GC只是简单检查了一下是否有-XX:+UseEpsilonGC。

CollectedHeap* EpsilonArguments::create_heap() { return create_heap_with_policy<EpsilonHeap, EpsilonCollectorPolicy>(); }

GCArguments::create_heap()更简单,直接根据堆的类型(EpsilonHeap,真正的Java堆空间表示)和垃圾回收器策略(EpsilonCollectorPolicy,堆初始化大小,最小,最大值,对齐等信息)创建堆。这个新创建的堆是EpsilonHeap,关于Java堆体系可以参见Java分代堆,简单来说,每种垃圾回收器都有一个独属于自己的可垃圾回收堆,它需要继承自CollectedHeap:

// hotspot\share\gc\epsilon\epsilonHeap.hpp class EpsilonHeap : public CollectedHeap { friend class VMStructs; private: // 回收器策略 EpsilonCollectorPolicy* _policy; // 软引用清除策略 SoftRefPolicy _soft_ref_policy; // perfdata支持 EpsilonMonitoringSupport* _monitoring_support; // 感知内存池使用情况 MemoryPool* _pool; GCMemoryManager _memory_manager; // 实际堆空间 ContiguousSpace* _space; // 虚拟内存(及其物理后备) VirtualSpace _virtual_space; // 最大TLAB size_t _max_tlab_size; // 间隔多少次内存分配再进行perdata数据更新 size_t _step_counter_update; // 间隔多少次内存分配再进行堆用量输出 size_t _step_heap_print; // TLAB大小衰减时间 int64_t _decay_time_ns; // 最后一次perdata数据更新计数 volatile size_t _last_counter_update; // 最后一次输出堆用量计数 volatile size_t _last_heap_print; public: ... };

CollectedHeap是一个抽象基类,里面有很多纯虚函数需要子类重写,创建完堆之后JVM会初始化堆,初始化分两步走:EpsilonHeap::initialize和EpsilonHeap::post_initialize。initialize是重头戏,它做了最重要的工作,包括堆内存的申请,gc barrier的设置:

// hotspot\share\gc\epsilon\epsilonHeap.hpp jint EpsilonHeap::initialize() { size_t align = _policy->heap_alignment(); size_t init_byte_size = align_up(_policy->initial_heap_byte_size(), align); size_t max_byte_size = align_up(_policy->max_heap_byte_size(), align); // 申请虚拟内存空间,然后commit一部分 // [------------------------------|------------------] // [ committed | reserved ] // 0 init_byte_size max_byte_size // low/low_boundary high high_boundary ReservedSpace heap_rs = Universe::reserve_heap(max_byte_size, align); _virtual_space.initialize(heap_rs, init_byte_size); MemRegion committed_region((HeapWord*)_virtual_space.low(), (HeapWord*)_virtual_space.high()); MemRegion reserved_region((HeapWord*)_virtual_space.low_boundary(), (HeapWord*)_virtual_space.high_boundary()); initialize_reserved_region(reserved_region.start(), reserved_region.end()); // 用ContiguousSpace表示这片(连续)堆内存 _space = new ContiguousSpace(); _space->initialize(committed_region, /* clear_space = */ true, /* mangle_space = */ true); // 计算最大tlab大小 _max_tlab_size = MIN2(CollectedHeap::max_tlab_size(), align_object_size(EpsilonMaxTLABSize / HeapWordSize)); _step_counter_update = MIN2<size_t>(max_byte_size / 16, EpsilonUpdateCountersStep); _step_heap_print = (EpsilonPrintHeapSteps == 0) ? SIZE_MAX : (max_byte_size / EpsilonPrintHeapSteps); _decay_time_ns = (int64_t) EpsilonTLABDecayTime * NANOSECS_PER_MILLISEC; // perfdata支持,外部可以访问共享内存感知堆信息 _monitoring_support = new EpsilonMonitoringSupport(this); _last_counter_update = 0; _last_heap_print = 0; // 创建gc barrier,比如CMS修改老年代指向新生代的指针就有一个write barrier // Epsilon GC只是用它在线程创建的时候初始化TLAB BarrierSet::set_barrier_set(new EpsilonBarrierSet()); // 完成初始化,输出配置信息 ... return JNI_OK; } void EpsilonHeap::post_initialize() { CollectedHeap::post_initialize(); } void EpsilonHeap::initialize_serviceability() { // post_initialize会调用该方法,将堆空间加入内存池管理 _pool = new EpsilonMemoryPool(this); _memory_manager.add_pool(_pool); } 3. 内存分配

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zyzswg.html