# G1的基本概念

## 分区

G1的分区类型大致可以分为四类：

#### 4.老生代分区

###### 其中新生代分区又可以分为Eden和Survivor;大对象分区又可以分为：大对象头分区和大对象连续分区。

// 判断是否是设置过堆分区大小，如果有则使用;
//没有，则根据初始内存和最大分配内存，获得平均值，并根据HR的个数得到分区的大小，和分区的下限比较，取两者的最大值。
void HeapRegion::setup_heap_region_size(size_t initial_heap_size, size_t max_heap_size) {
uintx region_size = G1HeapRegionSize;
if (FLAG_IS_DEFAULT(G1HeapRegionSize)) {
size_t average_heap_size = (initial_heap_size + max_heap_size) / 2;
region_size = MAX2(average_heap_size / TARGET_REGION_NUMBER,
(uintx) MIN_REGION_SIZE);
}

//对region_size按2的幂次对齐，并且保证其落在上下限范围内。
int region_size_log = log2_long((jlong) region_size);
// Recalculate the region size to make sure it's a power of
// 2. This means that region_size is the largest power of 2 that's
// <= what we've calculated so far.
region_size = ((uintx)1 << region_size_log);

//确保region_size落在[1MB,32MB]之间
// Now make sure that we don't go over or under our limits.
if (region_size < MIN_REGION_SIZE) {
region_size = MIN_REGION_SIZE;
} else if (region_size > MAX_REGION_SIZE) {
region_size = MAX_REGION_SIZE;
}

// 根据region_size 计算一些变量，比如卡表大小
// And recalculate the log.
region_size_log = log2_long((jlong) region_size);

// Now, set up the globals.
guarantee(LogOfHRGrainBytes == 0, "we should only set it once");
LogOfHRGrainBytes = region_size_log;

guarantee(LogOfHRGrainWords == 0, "we should only set it once");
LogOfHRGrainWords = LogOfHRGrainBytes - LogHeapWordSize;

guarantee(GrainBytes == 0, "we should only set it once");
// The cast to int is safe, given that we've bounded region_size by
// MIN_REGION_SIZE and MAX_REGION_SIZE.
GrainBytes = (size_t)region_size;

guarantee(GrainWords == 0, "we should only set it once");
GrainWords = GrainBytes >> LogHeapWordSize;
guarantee((size_t) 1 << LogOfHRGrainWords == GrainWords, "sanity");

guarantee(CardsPerRegion == 0, "we should only set it once");
CardsPerRegion = GrainBytes >> CardTableModRefBS::card_shift;


2048 X 32MB =64GB。假设设置xms=32G，xmx=128G，则每个堆分区的大小为32M，分区个数动态变化范围从1024到4096个。

region_size的一半以上的大对象直接进入老生代。

#### 新生代大小

//初始化新生代大小参数，根据不同的jvm参数判断计算新生代大小，供后续使用。
_min_desired_young_length(0), _max_desired_young_length(0)    {
//如果设置了NewRatio且同时设置NewSize或MaxNewSize的情况下，则NewRatio被忽略
if (FLAG_IS_CMDLINE(NewRatio)) {
if (FLAG_IS_CMDLINE(NewSize) || FLAG_IS_CMDLINE(MaxNewSize)) {
warning("-XX:NewSize and -XX:MaxNewSize override -XX:NewRatio");
} else {
_sizer_kind = SizerNewRatio;
return;
}
}

//参数传递有问题，最小值大于最大值
if (NewSize > MaxNewSize) {
if (FLAG_IS_CMDLINE(MaxNewSize)) {
warning("NewSize (" SIZE_FORMAT "k) is greater than the MaxNewSize (" SIZE_FORMAT "k). "
"A new max generation size of " SIZE_FORMAT "k will be used.",
NewSize/K, MaxNewSize/K, NewSize/K);
}
MaxNewSize = NewSize;
}

//根据参数计算分区个数
if (FLAG_IS_CMDLINE(NewSize)) {
_min_desired_young_length = MAX2((uint) (NewSize / HeapRegion::GrainBytes),
1U);
if (FLAG_IS_CMDLINE(MaxNewSize)) {
_max_desired_young_length =
MAX2((uint) (MaxNewSize / HeapRegion::GrainBytes),
1U);
_sizer_kind = SizerMaxAndNewSize;
} else {
_sizer_kind = SizerNewSizeOnly;
}
} else if (FLAG_IS_CMDLINE(MaxNewSize)) {
_max_desired_young_length =
MAX2((uint) (MaxNewSize / HeapRegion::GrainBytes),
1U);
_sizer_kind = SizerMaxNewSizeOnly;
}
}

//使用G1NewSizePercent来计算新生代的最小值
uint G1YoungGenSizer::calculate_default_min_length(uint new_number_of_heap_regions) {
uint default_value = (new_number_of_heap_regions * G1NewSizePercent) / 100;
return MAX2(1U, default_value);
}

//使用G1MaxNewSizePercent来计算新生代的最大值
uint G1YoungGenSizer::calculate_default_max_length(uint new_number_of_heap_regions) {
uint default_value = (new_number_of_heap_regions * G1MaxNewSizePercent) / 100;
return MAX2(1U, default_value);
}

//这里根据不同的参数输入来计算大小
//recalculate_min_max_young_length在初始化时被调用，在堆空间改变时也会被调用
void G1YoungGenSizer::recalculate_min_max_young_length(uint number_of_heap_regions, uint* min_young_length, uint* max_young_length) {
assert(number_of_heap_regions > 0, "Heap must be initialized");

switch (_sizer_kind) {
case SizerDefaults:
*min_young_length = calculate_default_min_length(number_of_heap_regions);
*max_young_length = calculate_default_max_length(number_of_heap_regions);
break;
case SizerNewSizeOnly:
*max_young_length = calculate_default_max_length(number_of_heap_regions);
*max_young_length = MAX2(*min_young_length, *max_young_length);
break;
case SizerMaxNewSizeOnly:
*min_young_length = calculate_default_min_length(number_of_heap_regions);
*min_young_length = MIN2(*min_young_length, *max_young_length);
break;
case SizerMaxAndNewSize:
// Do nothing. Values set on the command line, don't update them at runtime.
break;
case SizerNewRatio:
*min_young_length = number_of_heap_regions / (NewRatio + 1);
*max_young_length = *min_young_length;
break;
default:
ShouldNotReachHere();
}


G1是自适应拓展空间的。

size_t G1CollectorPolicy::expansion_amount() {
//根据历史信息获取平均GC时间
double recent_gc_overhead = recent_avg_pause_time_ratio() * 100.0;
//G1 GC时间与应用时间占比超过阈值才需要动态扩展
//这个阈值的值为10% 上文提过计算方式
// We will double the existing space, or take
// G1ExpandByPercentOfAvailable % of the available expansion
// space, whichever is smaller, bounded below by a minimum
// expansion (unless that's all that's left.)
const size_t min_expand_bytes = 1*M;
size_t reserved_bytes = _g1->max_capacity();
size_t committed_bytes = _g1->capacity();
size_t uncommitted_bytes = reserved_bytes - committed_bytes;
size_t expand_bytes;
size_t expand_bytes_via_pct =
uncommitted_bytes * G1ExpandByPercentOfAvailable / 100;
expand_bytes = MIN2(expand_bytes_via_pct, committed_bytes);
expand_bytes = MAX2(expand_bytes, min_expand_bytes);
expand_bytes = MIN2(expand_bytes, uncommitted_bytes);

......
} else {
return 0;
}
}
//G1内存拓展时间书后面部分会介绍


## G1停顿预测模型

G1是一个响应优先的GC算法，用户可以设定期望停顿时间由参数MaxGCPauseMills控制，默认值为200ms。
G1会在这个目标停顿时间内完成垃圾回收的工作。

G1使用停顿预测模型来满足期望，预测逻辑基于衰减平均值和衰减标准差。

## 卡表和位图

GC最早引入卡表是为了对内存的引用关系做标记，从而根据引用关系快速遍历活跃对象。

G1中还使用了bitmap,用bitmap可以描述一个分区对另外一个分区的引用情况，也可以描述内存分配的情况。

## 对象头

java代码首先被翻译成字节码(bytecode)，在JVM执行时才能确定要执行函数的地址，如何实现java的多态调用，最直观的想法是把java对象映射成C++对象或者封装成C++对象，比如增加一个额外的对象头，里面指向一个对象，而这个对象存储了java代码的地址。

class oopDesc {
friend class VMStructs;
private:
volatile markOop  _mark;
Klass*      _klass;
narrowKlass _compressed_klass;

//静态变量用于快速访问BarrierSet
static BarrierSet* _bs;


1.标记信息

1.使用了偏向锁，并且偏向锁被设置了
2.对象被加锁了
3.对象被设置了hash_code

2.元数据信息

## 内存分配和管理

JVM通过操作系统的系统调用进行内存的申请，典型的就是mmap。

mmap使用PAGE_SIZE为单位来进行映射，而内存也只能以页为单位进行映射，若要映射非PAGE_SIZE整数倍的地址范围，要先进行内存对齐，强行映射。

JVM常见对象类型

ResourceObj：线程有一个资源空间，一般ResourceObj都位于这里。定义资源空间的目的是对JVM其他功能的支持，如CFG、在C1/C2优化时可能需要访问运行时信息（这些信息可以保存在线程的资源区）。

StackObj：栈对象，声明的对象使用栈管理。其实例对象并不提供任何功能，且禁止New/Delete操作。对象分配在线程栈中，或者使用自定义的栈容器进行管理。

ValueObj：值对象，该对象在堆对象需要进行嵌套时使用，简单地说就是对象分配的位置和宿主对象（即拥有）是一样的。

AllStatic: 静态对象，全局对象，只有一个。值得一提的是C++初始化没有通过规范保证，可能会有两个静态对象相互依赖的问题，初始化时可能会出错。JVM中很多静态对象初始化都是显示调用静态初始化函数。

MetaspaceObj: 元对象，比如InstanceKlass这样的元数据就是元对象。

CHeapObj:

  mtNone              = 0x0000,  // undefined
mtClass             = 0x0100,  // JVM中java类
mtCode              = 0x0400,  // JVM中生成的编译代码
mtGC                = 0x0500,  // GC的内存
mtCompiler          = 0x0600,  // 编译器使用的内存
mtInternal          = 0x0700,  // JVM中内部使用的类型，不属于上述类型。

mtOther             = 0x0800,  // 不是由JVM使用的内存
mtSymbol            = 0x0900,  //符号表使用内存
mtNMT               = 0x0A00,  // mNMT使用内存
mtChunk             = 0x0B00,  // chunk用于缓存
mtJavaHeap          = 0x0C00,  // Java 堆
mtClassShared       = 0x0D00,  // 共享类数据
mtTest              = 0x0E00,  // Test type for verifying NMT
mtTracing           = 0x0F00,  // memory used for Tracing
mt_number_of_types  = 0x000F,  // number of memory types (mtDontTrack
// is not included as validate type)
mtDontTrack         = 0x0F00,  // memory we do not or cannot track


## 线程

JVM线程图 如上

JVM线程状态

//新创建线程
case NEW
: return "NEW";
//可运行或者正在运行
case RUNNABLE                 : return "RUNNABLE";
case SLEEPING                 : return "TIMED_WAITING (sleeping)";
//调用Object.wait()进入等待
case IN_OBJECT_WAIT           : return "WAITING (on object monitor)";
//调用Object.wait(long)进入等待且有过期时间
case IN_OBJECT_WAIT_TIMED     : return "TIMED_WAITING (on object monitor)";
//JVM内部调用LockSupport.park()进入等待
case PARKED                   : return "WAITING (parking)";
//JVM内部调用LockSupport.park()进入等待，且有过期时间
case PARKED_TIMED             : return "TIMED_WAITING (parking)";
//进入一个同步块
case BLOCKED_ON_MONITOR_ENTER : return "BLOCKED (on object monitor)";
//终止
case TERMINATED               : return "TERMINATED";
default                       : return "UNKNOWN";


  ALLOCATED,                    // 分配了但未初始化
INITIALIZED,                  // 初始化完未启动
RUNNABLE,                     //  已经启动并可被执行或者正在运行
MONITOR_WAIT,                 // 等待一个Monitor
CONDVAR_WAIT,                 // 等待一个条件变量
OBJECT_WAIT,                  // 通过调用Object.wait()等待对象
BREAKPOINTED,                 //调式状态
ZOMBIE                        // 僵尸状态，等待回收



### 栈帧

  _pc = NULL;//程序计数器，指向下一个要执行的代码地址
_sp = NULL;//栈顶指针
_unextended_sp = NULL;//异常栈顶指针
_fp = NULL;//栈底指针
_cb = NULL;//代码块的地址
_deopt_state = unknown;//这个字段描述从编译代码到解释代码反优化的状态


### 句柄

JVM设计了handleArea，这是一块线程的资源区，在这个区域分配句柄并管理所有的句柄,如果函数还在调用中，那么句柄有效，句柄关联的对象也就是活跃对象。

06-22
04-09 122

11-19
08-01 561
08-25
04-17 65