JVM 中的垃圾回收器通过可达性分析来探索所有存活的对象扫描堆中的对象,看能否沿着 GC Root 对象为起点的引用链找到该对象,如果找不到,则表示可以回收可以作为 GC Root 的对象
虚拟机栈(栈帧中的本地变量表)中引用的对象。方法区中类静态属性引用的对象方法区中常量引用的对象本地方法栈中 JNI(即一般说的Native方法)引用的对象 1.3 四种引用
- 强引用
只有所有 GC Roots 对象都不通过 【强引用】 引用该对象,该对象才能被垃圾回收
- 软引用(SoftReference)
仅有软引用引用该对象时,在垃圾回收后,内存仍不足时会再次出发垃圾回收,回收软引用对象
可以配合引用队列来释放软引用自身
- 弱引用(WeakReference)
仅有弱引用引用该对象时,在垃圾回收时,无论内存是否充足,都会回收弱引用对象
可以配合引用队列来释放弱引用自身
- 虚引用(PhantomReference)
必须配合引用队列使用,主要配合 ByteBuffer 使用,被引用对象回收时,会将虚引用入队,
由 Reference Handler 线程调用虚引用相关方法释放直接内存
- 终结器引用(FinalReference)
无需手动编码,但其内部配合引用队列使用,在垃圾回收时,终结器引用入队(被引用对象暂时没有被回收),再由 Finalizer 线程通过终结器引用找到被引用对象并调用它的 finalize 方法,第二次 GC 时才能回收被引用对象。
2.垃圾回收算法 2.1 标记清楚定义:Mark Sweep
速度较快会产生内存碎片
2.2 标记整理Mark Compact
速度慢没有内存碎片 2.3 复制
Copy
不会有内存碎片需要占用两倍内存空间
3.分代垃圾回收新创建的对象首先分配在 eden 区(伊甸园区)eden 区空间不足时,触发 minor gc ,eden 区 和 from 区存活的对象使用 copy 复制到 to 中,存活的对象年龄加1,然后交换 from区 和 to区minor gc 会引发 stop the world,暂停其他线程,等垃圾回收结束后,恢复用户线程运行当幸存区对象的寿命超过阈值时,会晋升到老年代,最大的寿命是 15(4bit)当老年代空间不足时,会先触发 minor gc,如果空间仍然不足,那么就触发 full gc ,停止的时间更长! 3.1 相关 VM 参数
| 含义 | 参数 |
|---|---|
| 堆初始大小 | -Xms |
| 堆最大大小 | -Xmx 或 -XX:MaxHeapSize=size |
| 新生代大小 | -Xmn 或 (-XX:NewSize=size + -XX:MaxNewSize=size ) |
| 幸存区比例(动态) | -XX:InitialSurvivorRatio=ratio 和 -XX:+UseAdaptiveSizePolicy |
| 幸存区比例 | -XX:SurvivorRatio=ratio |
| 晋升阈值 | -XX:MaxTenuringThreshold=threshold |
| 晋升详情 | -XX:+PrintTenuringDistribution |
| GC详情 | -XX:+PrintGCDetails -verbose:gc |
| FullGC 前 MinorGC | -XX:+ScavengeBeforeFullGC |
单线程堆内存较小,适合个人电脑(CPU个数少)
-XX:+UseSerialGC= Serial + SerialOld 复制 标记-整理法4.2 吞吐量优先
多线程堆内存较大,多核CPU让单位时间内,STW的时间最短 0.2 0.2 = 0.4
-XX:+UseParallelGC ~ -XX:+UsePrallerOldGC -XX:+UseAdaptiveSizePolicy -XX:GCTimeRatio=ratio // 1/(1+radio) -XX:MaxGCPauseMillis=ms // 200ms -XX:ParallelGCThreads=n4.3 响应时间优先
多线程堆内存较大,多核CPU尽可能让 STW 的单次时间最短 0.1 0.1 0.1 0.1 0.1 = 0.5
-XX:+UseConcMarkSweepGC ~ -XX:+UseParNewGC ~ SerialOld -XX:ParallelGCThreads=n ~ -XX:ConcGCThreads=threads -XX:CMSInitiatingOccupancyFraction=percent -XX:+CMSScavengeBeforeRemark
CMS 收集器
Concurrent Mark Sweep,一种以获取最短回收停顿时间为目标的老年代收集器
特点:基于标记-清除算法实现。并发收集、低停顿,但是会产生内存碎片
应用场景:适用于注重服务的响应速度,希望系统停顿时间最短,给用户带来更好的体验等场景下。如 web 程序、b/s 服务
CMS 收集器的运行过程分为下列4步:
**初始标记:**标记 GC Roots 能直接到的对象。速度很快但是仍存在 Stop The World 问题。
**并发标记:**进行 GC Roots Tracing 的过程,找出存活对象且用户线程可并发执行。
**重新标记:**为了修正并发标记期间因用户程序继续运行而导致标记产生变动的那一部分对象的标记记录。仍然存在 Stop The World 问题
**并发清除:**对标记的对象进行清除回收,清除的过程中,可能任然会有新的垃圾产生,这些垃圾就叫浮动垃圾,如果当用户需要存入一个很大的对象时,新生代放不下去,老年代由于浮动垃圾过多,就会退化为 serial Old 收集器,将老年代垃圾进行标记-整理,当然这也是很耗费时间的!
CMS 收集器的内存回收过程是与用户线程一起并发执行的,可以搭配 ParNew 收集器(多线程,新生代,复制算法)与 Serial Old 收集器(单线程,老年代,标记-整理算法)使用。
定义: Garbage First
适用场景:
同时注重吞吐量(Throughput)和低延迟(Low latency),默认的暂停目标是 200 ms超大堆内存(内存大的),会将堆内存划分为多个大小相等的区域整体上是标记-整理算法,两个区域之间是复制算法
相关JVM参数:
JDK8 并不是默认开启的,所需要参数开启
-XX:+UseG1GC -XX:G1HeapRegionSize=size -XX:MaxGCPauseMillis=time4.4.1 G1垃圾回收阶段 4.4.2 Young Collection:
对新生代垃圾收集
会STW
E:eden,S:幸存区,O:老年代
如果老年代内存到达一定的阈值了,新生代垃圾收集同时会执行一些并发的标记。
在 Young GC 时会进行 GC Root 的初始化标记老年代占用堆空间比例达到阈值时,进行并发标记(不会STW),由下面的 JVM 参数决定 -XX:InitiatingHeapOccupancyPercent=percent (默认45%)
4.4.4 Mixed Collection:会对新生代 + 老年代 + 幸存区等进行混合收集,然后收集结束,会重新进入新生代收集。
会对 E S O 进行全面的回收
最终标记(Remark)会 STW拷贝存活(Evacuation)会 STW
-XX:MaxGCPauseMills=xxms 用于指定最长的停顿时间,因为指定了停顿时间。会回收价值高(回收后可以释放更多空间)的老年代
SerialGC
新生代内存不足发生的垃圾收集 - minor gc老年代内存不足发生的垃圾收集 - full gc ParallelGC
新生代内存不足发生的垃圾收集 - minor gc老年代内存不足发生的垃圾收集 - full gc CMS
新生代内存不足发生的垃圾收集 - minor gc老年代内存不足 G1
新生代内存不足发生的垃圾收集 - minor gc老年代内存不足 4.4.6 Young Collection 跨代引用
新生代回收的跨代引用(老年代引用新生代)问题
卡表 与 Remembered Set
Remembered Set 存在于E中,用于保存新生代对象对应的脏卡
脏卡:O 被划分为多个区域(一个区域512K),如果该区域引用了新生代对象,则该区域被称为脏卡 在引用变更时通过 post-write barried + dirty card queueconcurrent refinement threads 更新 Remembered Set
4.4.7 Remark重新标记阶段
在垃圾回收时,收集器处理对象的过程中
黑色:已被处理,需要保留的灰色:正在处理中的白色:还未处理的
5.垃圾回收调优 5.1 调优领域内存锁竞争cpu 占用iogc 5.2 确定目标
低延迟/高吞吐量? 选择合适的回收器
CMS G1 ZGCParallelGCZing 5.3 最快的GC是不发生GC
首先排除减少因为自身编写的代码而引发的内存问题
查看 Full GC 前后的内存占用,考虑以下几个问题
数据是不是太多?
resultSet = statement.executeQuery(“select * from 大表 limit n”) 数据表示是否太臃肿
对象图对象大小 16 Integer 24 int 4 是否存在内存泄漏
static Map map …软弱第三方缓存实现 5.4 新生代调优
新生代的特点
所有的 new 操作分配内存都是非常廉价的
TLAB thread-lcoal allocation buffer 死亡对象回收零代价大部分对象用过即死(朝生夕死)Minor GC 所用时间远小于 Full GC
新生代内存越大越好么?
不是
新生代内存太小:频繁触发 Minor GC ,会 STW ,会使得吞吐量下降新生代内存太大:老年代内存占比有所降低,会更频繁地触发 Full GC。而且触发 Minor GC 时,清理新生代所花费的时间会更长 新生代内存设置为内容纳[并发量*(请求-响应)]的数据为宜
幸存区需要能够保存 当前活跃对象+需要晋升的对象
晋升阈值配置得当,让长时间存活的对象尽快晋升
以 CMS 为例:
CMS 的老年代内存越大越好
先尝试不做调优,如果没有 Full GC 那么已经…,否者先尝试调优新生代。
观察发现 Full GC 时老年代内存占用,将老年代内存预设调大 1/4 ~ 1/3
-XX:CMSInitiatingOccupancyFraction=percent
根据黑马程序员JVM课程,编写笔记
https://www.bilibili.com/video/BV1yE411Z7AP?p=1&share_source=copy_web



