GC 及 JVM Tuning
GC的基础知识
#### 1.什么是垃圾

> C语言申请内存:malloc free
>
> C++: new delete
>
> c/C++ 手动回收内存-比较精确-开发效率低
>
> Java: new ?
>
> 自动内存回收-编程上简单-系统不容易出错-手动释放内存-容易出两种类型的问题:
>
> 1. 忘记回收
> 2. 多次回收
没有任何引用指向的一个对象或者多个对象(循环引用)

#### 2.如何定位垃圾

引用计数(ReferenceCount) 不能解决循环引用-可能都是1

根可达算法(RootSearching) 
#### 3.常见的垃圾回收算法

标记清除(mark sweep) - 没用的标记出来-直接清掉-其他不动
不适合伊甸区
位置不连续-容易产生碎片-效率偏低(两遍扫描:1.找出有用的 2。找出没用的)
算法相对简单-存活对象比较多的情况下效率较高

拷贝算法 (copying) - 有用的拷贝过来
适合伊甸区
浪费空间
移动复制对象-需要调整对象引用
适用于存活对象较少的情况-只扫描一次-效率提高
!image-20200514202929940
标记压缩(mark compact) - 有用的聚到一起-没用的清掉-空间是连续的-慢
没有碎片-方便对象分配
不会产生内存减半
需要移动对象-效率偏低(两遍扫描-指针需要调整)

#### 4.JVM内存分代模型(用于分代垃圾回收算法)
部分垃圾回收器使用的模型
新生代大量复制-少量存活-采用‘复制’算法 老年代存活率高-回收较少-采用‘标记清除’或‘标记压缩’
> 除Epsilon ZGC Shenandoah之外的GC都是使用逻辑分代模型
>
> G1是逻辑分代-物理不分代
>
> 除此之外不仅逻辑分代-而且物理分代
逻辑分代:
新生代大量复制-少量存活-采用‘复制’算法
老年代存活率高-回收较少-采用‘标记清除’或‘标记压缩’
新生代 + 老年代 + 永久代(1.7)Perm Generation/ 元数据区(1.8) Metaspace
1. 永久代 元数据 - Class
2. 永久代必须指定大小限制 -元数据可以设置-也可以不设置-无上限(受限于物理内存)
3. 字符串常量 1.7 - 永久代-1.8 - 堆
4. MethodArea逻辑概念 - 永久代、元数据
新生代 = Eden + 2个suvivor区
默认8:1:1
1. YGC回收之后-大多数的对象会被回收-活着的进入s0
2. 再次YGC-活着的对象eden + s0 -> s1
3. 再次YGC-eden + s1 -> s0
4. 年龄足够 -> 老年代 (15 CMS 6)
5. s区装不下 -> 老年代
老年代
1. 顽固分子
2. 老年代满了FGC Full GC
GC Tuning (Generation)
1. 尽量减少FGC
2. MinorGC = YGC:年轻代空间耗尽时触发
3. MajorGC = FullGC:在老年代无法继续分配空间时触发-新生代
4. -Xms 最小内存
5. -Xmx 最大内存
对象分配过程图
1. 首先尝试栈上分配-分配不下-进入伊甸区
2. 1次垃圾回收之后-进入survivor幸存区-来回复制
3. 多次垃圾回收之后-进入old去



什么情况下-栈上分配(无需人工调整)
1. 线程私有的小对象
2. 无逃逸:只在某一段代码使用-没有被外部引用所引用

3. 支持标量替换:用普通属性代替整个对象
什么情况下-线程本地分配TLAB
1. 每个线程在eden取1%的空间-分配对象时-优先往这块空间分配
###### 何时进入老年代

Mark word对象头中-GC的Age是4位-最大15-不能调大
Eden + S1 进入S2-超过S2的50%-年龄最大的放进Old

动态年龄:(不重要)
https://www.jianshu.com/p/989d3b06a49d
分配担保:(不重要)
YGC期间 survivor区空间不够了 空间担保直接进入老年代
参考:
https://cloud.tencent.com/developer/article/1082730#### 5.常见的垃圾回收器

JDK诞生 Serial(单线程)第一个诞生 提高效率-诞生了PS-为了配合CMS-诞生了PN-CMS是1.4版本后期引入-CMS是里程碑式的GC-它开启了并发回收的过程-但是CMS毛病较多-因此目前任何一个JDK版本默认是CMS
并发垃圾回收是因为无法忍受STW
常见组合:这些逻辑上-物理上都分代

G1:只在逻辑上分代
Serial(单线程) 年轻