一.GC策略检查
一个JVM实例在创建之前是要经过大量的配置信息检查的, 来检查用户对jvm配置的正确性、合理性. 不正确的配置, JVM会创建失败,java程序也就无法运行了; 不合理的配置, JVM会调整. 既然本文主要讲的是GC,那就以GC配置为例吧. 如果用户同时配置 -XX:+UseSerialGC -XX:+UseParallelGC 两种GC策略,则JVM无法启动, 并会抛出错误信息:
Conflicting collector combinations in option list; please refer to the release notes for the combinations allowed
Could not create the Java virtual machine.
抛出这种错误的主要原因是这两种GC策略或这叫内存堆管理器是不能兼容的,只能取其一. 关于GC策略配置检查的代码如下:
/**
* 检查GC日志信息输出文件的配置
*/
void check_gclog_consistency() {
if (UseGCLogFileRotation) { //开启日志文件切分
if ((Arguments::gc_log_filename() == NULL) ||
(NumberOfGCLogFiles == 0) ||
(GCLogFileSize == 0)) {
jio_fprintf(defaultStream::output_stream(),
"To enable GC log rotation, use -Xloggc:<filename> -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=<num_of_files> -XX:GCLogFileSize=<num_of_size>\n"
"where num_of_file > 0 and num_of_size > 0\n"
"GC log rotation is turned off\n");
UseGCLogFileRotation = false;
}
}
if (UseGCLogFileRotation && GCLogFileSize < 8*K) { //在开启日志文件切分的情况下,调整日志文件的大小
FLAG_SET_CMDLINE(uintx, GCLogFileSize, 8*K);
jio_fprintf(defaultStream::output_stream(),
"GCLogFileSize changed to minimum 8K\n");
}
}
// Check consistency of GC selection
/**
* 检查GC策略的配置是否正确
*/
bool Arguments::check_gc_consistency() {
check_gclog_consistency();
bool status = true;
// Ensure that the user has not selected conflicting sets
// of collectors. [Note: this check is merely a user convenience;
// collectors over-ride each other so that only a non-conflicting
// set is selected; however what the user gets is not what they
// may have expected from the combination they asked for. It's
// better to reduce user confusion by not allowing them to
// select conflicting combinations.
uint i = 0;
if (UseSerialGC) i++;
if (UseConcMarkSweepGC || UseParNewGC) i++;
if (UseParallelGC || UseParallelOldGC) i++;
if (UseG1GC) i++;
if (i > 1) {
jio_fprintf(defaultStream::error_stream(),
"Conflicting collector combinations in option list; "
"please refer to the release notes for the combinations "
"allowed\n");
status = false;
}
return status;
}
check_gc_consistency()方法就是来判断哪些GC策略是可以同时使用的, 哪些GC策略是相互冲突的.
二.内存堆管理器创建
这里的内存堆管理器CollectedHeap主要包括两部分, 一是内存分配策略;二是内存垃圾回收策略. 本节不会介绍任何具体的内存堆管理器实现, 而只是简单的介绍一下内存堆管理器的创建.至于JVM实例使用何种内存堆管理器, 是由用户或JVM的默认配置决定的,内存堆管理器的创建过程如下:
CollectedHeap* Universe::_collectedHeap = NULL;
/**
* 初始化内存堆及其内存管理器
*/
jint Universe::initialize_heap() {
printf("%s[%d] [tid: %lu]: 开始创建JVM的垃圾回收器...\n", __FILE__, __LINE__, pthread_self());
if (UseParallelGC) { //并行回收Gc
#ifndef SERIALGC
printf("%s[%d] [tid: %lu]: 试图创建内存堆管理器(ParallelScavengeHeap)...\n", __FILE__, __LINE__, pthread_self());
Universe::_collectedHeap = new ParallelScavengeHeap();
#else // SERIALGC
fatal("UseParallelGC not supported in java kernel vm.");
#endif // SERIALGC
} else if (UseG1GC) {
#ifndef SERIALGC
printf("%s[%d] [tid: %lu]: 试图创建GC策略(G1CollectorPolicy)...\n", __FILE__, __LINE__, pthread_self());
G1CollectorPolicy* g1p = new G1CollectorPolicy();
printf("%s[%d] [tid: %lu]: 试图创建内存堆管理器(G1CollectedHeap)...\n", __FILE__, __LINE__, pthread_self());
G1CollectedHeap* g1h = new G1CollectedHeap(g1p);
Universe::_collectedHeap = g1h;
#else // SERIALGC
fatal("UseG1GC not supported in java kernel vm.");
#endif // SERIALGC
} else {
GenCollectorPolicy *gc_policy;
if (UseSerialGC) {
printf("%s[%d] [tid: %lu]: 试图创建GC策略(MarkSweepPolicy)...\n", __FILE__, __LINE__, pthread_self());
gc_policy = new MarkSweepPolicy();
} else if (UseConcMarkSweepGC) {
#ifndef SERIALGC
if (UseAdaptiveSizePolicy) {
printf("%s[%d] [tid: %lu]: 试图创建GC策略(ASConcurrentMarkSweepPolicy)...\n", __FILE__, __LINE__, pthread_self());
gc_policy = new ASConcurrentMarkSweepPolicy();
} else {
printf("%s[%d] [tid: %lu]: 试图创建GC策略(ConcurrentMarkSweepPolicy)...\n", __FILE__, __LINE__, pthread_self());
gc_policy = new ConcurrentMarkSweepPolicy();
}
#else // SERIALGC
fatal("UseConcMarkSweepGC not supported in java kernel vm.");
#endif // SERIALGC
} else { // default old generation
printf("%s[%d] [tid: %lu]: 试图创建GC策略(MarkSweepPolicy)...\n", __FILE__, __LINE__, pthread_self());
gc_policy = new MarkSweepPolicy();
}
printf("%s[%d] [tid: %lu]: 试图创建内存堆管理器(GenCollectedHeap)...\n", __FILE__, __LINE__, pthread_self());
Universe::_collectedHeap = new GenCollectedHeap(gc_policy);
}
printf("%s[%d] [tid: %lu]: 试图初始化内存堆管理器...\n", __FILE__, __LINE__, pthread_self());
//开始初始化内存堆
jint status = Universe::heap()->initialize();
if (status != JNI_OK) {
return status;
}
#ifdef _LP64
if (UseCompressedOops) { //使用对象地址(指针)压缩
// Subtract a page because something can get allocated at heap base.
// This also makes implicit null checking work, because the
// memory+1 page below heap_base needs to cause a signal.
// See needs_explicit_null_check.
// Only set the heap base for compressed oops because it indicates
// compressed oops for pstack code.
bool verbose = PrintCompressedOopsMode || (PrintMiscellaneous && Verbose);
if (verbose) {
tty->cr();
tty->print("heap address: " PTR_FORMAT ", size: " SIZE_FORMAT " MB",
Universe::heap()->base(), Universe::heap()->reserved_region().byte_size()/M);
}
if ((uint64_t)Universe::heap()->reserved_region().end() > OopEncodingHeapMax) {
// Can't reserve heap below 32Gb.
Universe::set_narrow_oop_base(Universe::heap()->base() - os::vm_page_size());
Universe::set_narrow_oop_shift(LogMinObjAlignmentInBytes);
if (verbose) {
tty->print(", Compressed Oops with base: "PTR_FORMAT, Universe::narrow_oop_base());
}
} else {
Universe::set_narrow_oop_base(0);
if (verbose) {
tty->print(", zero based Compressed Oops");
}
#ifdef _WIN64
if (!Universe::narrow_oop_use_implicit_null_checks()) {
// Don't need guard page for implicit checks in indexed addressing
// mode with zero based Compressed Oops.
Universe::set_narrow_oop_use_implicit_null_checks(true);
}
#endif // _WIN64
if((uint64_t)Universe::heap()->reserved_region().end() > NarrowOopHeapMax) {
// Can't reserve heap below 4Gb.
Universe::set_narrow_oop_shift(LogMinObjAlignmentInBytes);
} else {
Universe::set_narrow_oop_shift(0);
if (verbose) {
tty->print(", 32-bits Oops");
}
}
}
if (verbose) {
tty->cr();
tty->cr();
}
}
assert(Universe::narrow_oop_base() == (Universe::heap()->base() - os::vm_page_size()) ||
Universe::narrow_oop_base() == NULL, "invalid value");
assert(Universe::narrow_oop_shift() == LogMinObjAlignmentInBytes ||
Universe::narrow_oop_shift() == 0, "invalid value");
#endif
// We will never reach the CATCH below since Exceptions::_throw will cause
// the VM to exit if an exception is thrown during initialization
//允许使用线程本地分配缓冲区
if (UseTLAB) {
//确保配置的内存堆管理器支持线程本地分配缓冲区
assert(Universe::heap()->supports_tlab_allocation(), "Should support thread-local allocation buffers");
ThreadLocalAllocBuffer::startup_initialization();
}
return JNI_OK;
}
从initialize_heap()方法可以看出,目前的hotspot JVM(1.7)主要有3中内存堆管理器的实现, 这3种内存堆管理器的配置和第一小节中的GC冲突策略配置是对应的,它们与上层抽象的内存堆管理器CollectedHeap的继承关系如下:
于上层而言, 内存堆管理器只负责响应上层因对象的创建而产生的内存申请请求,至于GC策略, 也只是内存堆管理器内部管理内存的一个方法而已.下面是关于内存堆管理器与其内部已实现的GC策略的列表(顺便博透一下)
配置参数
内存堆实现
回收策略
UseParallelGC
ParallelScavengerHeap
UseG1GC
G1CollectedHeap
G1CollectorPolicy
UseSerialGC
GenCollectedHeap
MarkSweepPolicy
UseComcMarkSweepGC
UseAdaptiveSizePolicy
ASConcurrentMarkSweepPolicy
默认
ConcurrentMarkSweepPolicy
默认
MarkSweepPolicy
回收策略
Young 内存代管理器
Old 内存代管理器
MarkSweepPolicy
UseParNewGC
ParallelGCThreads > 0
ParNewGeneration
TenuredGeneration
默认
DefNewGeneration
默认
ConcurrentMarkSweepPolicy
UseParNewGC
ParallelGCThreads > 0
ParNewGeneration
ConcurrentMarkSweepGeneration
默认
DefNewGeneration
默认
ASConcurrentMarkSweepPolicy
UseParNewGC
ParallelGCThreads > 0
ASParNewGeneration
ASConcurrentMarkSweepGeneration
默认
DefNewGeneration
默认
三.内存分配CollectedHeap
内存堆管理器负责分配的对象内存主要分为两类,一类是普通对象, 一类是类型描述信息对象. 普通对象一般具有零时性, 生命周期很短; 而类型描述信息对象可能会存在于真个JVM实例的生命进程中.正是 基于这种合理的假设, 就有了非常经典的内存分代管理的思想, 如新生代, 旧生代, 永久代等. 一种java类型可以实例化出很多对应的普通对象, 但一般只有一个对应的描述信息对象.
1.为普通对象分配存储空间
内存堆管理器CollectedHeap为普通对象或数组对象分配内存的过程大致相同, 主要分为两个, 一是分配内存,二是对这块内存做一些初始化工作. 以普通对象为例:
/**
* 为某一类型的实例对象申请一块非永久性存储空间
*/
oop CollectedHeap::obj_allocate(KlassHandle klass, int size, TRAPS) {
debug_only(check_for_valid_allocation_state());
//确保内存堆当前没有或将要发生Gc
assert(!Universe::heap()->is_gc_active(), "Allocation during gc not allowed");
assert(size >= 0, "int won't convert to size_t");
printf("%s[%d] [tid: %lu]: 开始为%s的实例分配存储空间...\n", __FILE__, __LINE__, pthread_self(), klass->name()->as_C_string());
HeapWord* obj = common_mem_allocate_init(size, CHECK_NULL);
//初始化对象
post_allocation_setup_obj(klass, obj, size);
NOT_PRODUCT(Universe::heap()->check_for_bad_heap_word_value(obj, size));
return (oop)obj;
}
(1).分配内存空间
/**
* 申请一块非永久性存储空间并初始化分配的对象内存块
*/
HeapWord* CollectedHeap::common_mem_allocate_init(size_t size, TRAPS) {
HeapWord* obj = common_mem_allocate_noinit(size, CHECK_NULL);
init_obj(obj, size);
return obj;
}
/**
* 申请一块非永久性存储空间,若分配失败则抛出异常
*
* 1).从本地线程分配缓冲[TLAB]中申请存储空间
* 2).从堆[Heap]中申请存储空间
*/
HeapWord* CollectedHeap::common_mem_allocate_noinit(size_t size, TRAPS) {
// Clear unhandled oops for memory allocation. Memory allocation might
// not take out a lock if from tlab, so clear here.
CHECK_UNHANDLED_OOPS_ONLY(THREAD->clear_unhandled_oops();)
if (HAS_PENDING_EXCEPTION) {
NOT_PRODUCT(guarantee(false, "Should not allocate with exception pending"));
return NULL; // caller does a CHECK_0 too
}
HeapWord* result = NULL;
if (UseTLAB) {
printf("%s[%d] [tid: %lu]: 试图从本地线程分配缓冲[TLAB]中申请 %lu bytes存储空间...\n", __FILE__, __LINE__, pthread_self(), size * HeapWordSize);
result = CollectedHeap::allocate_from_tlab(THREAD, size);
if (result != NULL) {
assert(!HAS_PENDING_EXCEPTION,"Unexpected exception, will result in uninitialized storage");
return result;
}
}
printf("%s[%d] [tid: %lu]: 试图从堆[Heap]中申请 %lu bytes存储空间...\n", __FILE__, __LINE__, pthread_self(), size * HeapWordSize);
//Gc是否超时
bool gc_overhead_limit_was_exceeded = false;
result = Universe::heap()->mem_allocate(size, &gc_overhead_limit_was_exceeded);
if (result != NULL) {
NOT_PRODUCT(Universe::heap()->check_for_non_bad_heap_word_value(result, size));
assert(!HAS_PENDING_EXCEPTION, "Unexpected exception, will result in uninitialized storage");
THREAD->incr_allocated_bytes(size * HeapWordSize);
return result;
}
//申请存储空间失败,抛出OutOfMemoryError异常
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());
} else {//本次分配过程,当前线程执行了GC操作,并且该GC超时了,抛出OutOfMemoryError异常
// -XX:+HeapDumpOnOutOfMemoryError and -XX:OnOutOfMemoryError support
report_java_out_of_memory("GC overhead limit exceeded");
if (JvmtiExport::should_post_resource_exhausted()) {
JvmtiExport::post_resource_exhausted(
JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR | JVMTI_RESOURCE_EXHAUSTED_JAVA_HEAP,
"GC overhead limit exceeded");
}
THROW_OOP_0(Universe::out_of_memory_error_gc_overhead_limit());
}
}
/**
* 初始化对象的内存块(除对象的标准头部之外,其余则"清零")
*/
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);
}
/**
* 从当前线程的本地分配缓冲区中分配指定大小的内存空间
*/
HeapWord* CollectedHeap::allocate_from_tlab(Thread* thread, size_t size) {
assert(UseTLAB, "should use UseTLAB");
HeapWord* obj = thread->tlab().allocate(size);
if (obj != NULL) {
return obj;
}
//当前线程的本地分配缓冲区剩余空间不够,则尝试慢分配方式
return allocate_from_tlab_slow(thread, size);
}
/**
* 申请一块新的本地分配缓冲区并顺带从中分配指定大小的内存块
*/
HeapWord* CollectedHeap::allocate_from_tlab_slow(Thread* thread, size_t size) {
// Retain tlab and allocate object in shared space if
// the amount free in the tlab is too large to discard.
//如果丢弃旧的本地分配缓冲区所带来的碎片大小大于设置的阈值,则放弃线程的缓冲区分配
if (thread->tlab().free() > thread->tlab().refill_waste_limit()) {
thread->tlab().record_slow_allocation(size);
return NULL;
}
//计算当前线程新本地分配缓冲区的大小
size_t new_tlab_size = thread->tlab().compute_size(size);
thread->tlab().clear_before_allocation();
if (new_tlab_size == 0) {
return NULL;
}
//试图为当前线程申请一块新的本地分配缓冲区
HeapWord* obj = Universe::heap()->allocate_new_tlab(new_tlab_size);
if (obj == NULL) {
return NULL;
}
//新分配的本地分配缓冲区"清零"
if (ZeroTLAB) {
// ..and clear it.
Copy::zero_to_words(obj, new_tlab_size);
} else {
// ...and zap just allocated object.
#ifdef ASSERT
// Skip mangling the space corresponding to the object header to
// ensure that the returned space is not considered parsable by
// any concurrent GC thread.
size_t hdr_size = oopDesc::header_size();
Copy::fill_to_words(obj + hdr_size, new_tlab_size - hdr_size, badHeapWordVal);
#endif // ASSERT
}
thread->tlab().fill(obj, obj + size, new_tlab_size);
return obj;
}
(2).初始化对象
/**
* 普通对象分配之后的初始化
*/
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);
}
/**
* 初始化实例对象的两个基本信息
* 1.标记信息
* 2.设置该实例对象类型所对应的类型描述对象
*/
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);
}
/**
* 初始化新分配对象的标记信息
*/
void CollectedHeap::post_allocation_setup_no_klass_install(KlassHandle klass,
HeapWord* objPtr,
size_t size) {
oop obj = (oop)objPtr;
assert(obj != NULL, "NULL object pointer");
if (UseBiasedLocking && (klass() != NULL)) {
obj->set_mark(klass->prototype_header());
} else {
// May be bootstrapping
obj->set_mark(markOopDesc::prototype());
}
}
/**
* 设置新分配对象的类型描述信息
*/
void CollectedHeap::post_allocation_install_obj_klass(KlassHandle klass,
oop obj,
int size) {
// These asserts are kind of complicated because of klassKlass
// and the beginning of the world.
assert(klass() != NULL || !Universe::is_fully_initialized(), "NULL klass");
assert(klass() == NULL || klass()->is_klass(), "not a klass");
assert(klass() == NULL || klass()->klass_part() != NULL, "not a klass");
assert(obj != NULL, "NULL object pointer");
obj->set_klass(klass());
assert(!Universe::is_fully_initialized() || obj->blueprint() != NULL,
"missing blueprint");
}
// Support for jvmti and dtrace
/**
* 向相关组件发布一个新对象的分配消息
*/
inline void post_allocation_notify(KlassHandle klass, oop obj) {
// support low memory notifications (no-op if not enabled)
LowMemoryDetector::detect_low_memory_for_collected_pools();
// support for JVMTI VMObjectAlloc event (no-op if not enabled)
JvmtiExport::vm_object_alloc_event_collector(obj);
if (DTraceAllocProbes) {
// support for Dtrace object alloc event (no-op most of the time)
if (klass() != NULL && klass()->klass_part()->name() != NULL) {
SharedRuntime::dtrace_object_alloc(obj);
}
}
}
2. 为类型的描述信息对象分配永久性的存储空间
/**
* 为类型的描述信息对象分配永久性的存储空间
*/
oop CollectedHeap::permanent_obj_allocate(KlassHandle klass, int size, TRAPS) {
//申请内存
oop obj = permanent_obj_allocate_no_klass_install(klass, size, CHECK_NULL);
//初始化对象(设置对象的类型信息)
post_allocation_install_obj_klass(klass, obj, size);
NOT_PRODUCT(Universe::heap()->check_for_bad_heap_word_value((HeapWord*) obj, size));
return obj;
}
/**
* 为类型的描述信息对象分配永久性的存储空间(不为该对象设置它的类型信息)
*/
oop CollectedHeap::permanent_obj_allocate_no_klass_install(KlassHandle klass,
int size,
TRAPS) {
debug_only(check_for_valid_allocation_state());
assert(!Universe::heap()->is_gc_active(), "Allocation during gc not allowed");
assert(size >= 0, "int won't convert to size_t");
//申请内存块并初始化它
HeapWord* obj = common_permanent_mem_allocate_init(size, CHECK_NULL);
//设置对象的标记信息
post_allocation_setup_no_klass_install(klass, obj, size);
#ifndef PRODUCT
const size_t hs = oopDesc::header_size();
Universe::heap()->check_for_bad_heap_word_value(obj+hs, size-hs);
#endif
return (oop)obj;
}
/**
* 分配指定大小的一块永久性存储空间并初始化该内存块
*/
HeapWord* CollectedHeap::common_permanent_mem_allocate_init(size_t size, TRAPS) {
HeapWord* obj = common_permanent_mem_allocate_noinit(size, CHECK_NULL);
init_obj(obj, size);
return obj;
}
/**
* 从永久代的存储空间中分配内存,不初始化该内存块
*/
HeapWord* CollectedHeap::common_permanent_mem_allocate_noinit(size_t size, TRAPS) {
if (HAS_PENDING_EXCEPTION) {
NOT_PRODUCT(guarantee(false, "Should not allocate with exception pending"));
return NULL; // caller does a CHECK_NULL too
}
#ifdef ASSERT
if (CIFireOOMAt > 0 && THREAD->is_Compiler_thread() &&
++_fire_out_of_memory_count >= CIFireOOMAt) {
// For testing of OOM handling in the CI throw an OOM and see how
// it does. Historically improper handling of these has resulted
// in crashes which we really don't want to have in the CI.
THROW_OOP_0(Universe::out_of_memory_error_perm_gen());
}
#endif
//申请永久性存储空间
HeapWord* result = Universe::heap()->permanent_mem_allocate(size);
if (result != NULL) {
NOT_PRODUCT(Universe::heap()-> check_for_non_bad_heap_word_value(result, size));
assert(!HAS_PENDING_EXCEPTION, "Unexpected exception, will result in uninitialized storage");
return result;
}
printf("%s[%d] [tid: %lu]: 申请 %lu bytes的永久性存储空间失败!!!\n", __FILE__, __LINE__, pthread_self(), size * HeapWordSize);
// -XX:+HeapDumpOnOutOfMemoryError and -XX:OnOutOfMemoryError support
report_java_out_of_memory("PermGen space");
if (JvmtiExport::should_post_resource_exhausted()) {
JvmtiExport::post_resource_exhausted(JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR, "PermGen space");
}
//抛出永久代的OOM错误
THROW_OOP_0(Universe::out_of_memory_error_perm_gen());
}
从上面抽象的内存堆管理器CollectedHeap为普通对象或类型描述信息对象分配内存的代码可以看出, 这个对象内存分配的过程中涉及到了三个重要的方法:mem_allocate, allocate_new_tlab,permanent_mem_allocate,显然, 这三个核心方法就交由不同的内存堆管理器来实现了.所以在后面的博文中,笔者将围绕着这三个方法的来详细地介绍每一个内存堆管理器的实现.
---------------------
作者:读程序的手艺人
来源:优快云
原文:https://blog.youkuaiyun.com/xhh198781/article/details/40680819
版权声明:本文为博主原创文章,转载请附上博文链接!