1.什么是垃圾
没有引用指向的对象
C语言申请内存:malloc free C++: new delete Java: new ? 自动内存回收,编程上简单,系统不容易出错,手动释放内存,容易出两种类型的问题:
- 忘记回收
- 多次回收
2.如何定位垃圾
- 引用计数 无法解决循环引用

- 根可达算法(根搜索算法 Root Searching) 解决循环引用
根对象是什么:
- 线程栈变量
main()会起一个main线程,线程里面有一个main栈针
,栈针里面会有变量
- 静态变量
常量池
Java中的常量池,实际上分为两种形态:**静态常量池**和**运行时常量池**。<br /> 所谓**静态常量池**,即*.class文件中的常量池,class文件中的常量池不仅仅包含字符串(数字)字面量,还包含类、方法的信息,占用class文件绝大部分空间。这种常量池主要用于存放两大类常量:**字面量**(Literal)和**符号引用量**(Symbolic References),字面量相当于Java语言层面常量的概念,如文本字符串,声明为final的常量值等,符号引用则属于编译原理方面的概念,包括了如下三种类型的常量:
- 类和接口的全限定名
- 字段名称和描述符
方法名称和描述符
而运行时常量池,则是jvm虚拟机在完成类装载操作后,将class文件中的常量池载入到内存中,并保存在方法区中,我们常说的常量池,就是指方法区中的运行时常量池。
c. JNI指针
调用C C++ 本地方法所用到的那些个对象3.常见的垃圾回收算法
- 标记清除(mark-sweep) - 位置不连续 产生碎片
算法相对简单,存活对象多的时候效率较高
两边扫描(先找不可回收的,标记,然后再找可回收的),效率偏低
容易产生碎片
- 拷贝算法 - 没有碎片,浪费空间

- 适用于存活对象较少的情况,只扫描一遍,效率提高,没有碎片
- 空间浪费,移动复制对象,需要调整引用
- 标记压缩 - 没有碎片,效率偏低
- 不会产生碎片,方便对象分配,不会产生内存减半
- 扫描两次(第一遍找出有用的块,第二遍移动,多线程可能还会有问题),需要移动对象,效率偏低
4.JVM内存分代模型(用于分代垃圾回收算法)

部分垃圾回收器使用的模型
除Epsilon ZGC Shenandonah 之外的GC都是使用逻辑分代模型 G1是逻辑分代 物理不分代 除此之外不仅逻辑分代,而且物理分代
新生代 + 老年代 + 永久代(1.7)/ 元数据区(1.8) Metaspace
- 永久代 元数据 - Class
- 永久代必须指定大小限制 ,元数据可以设置,也可以不设置,无上限(受限于物理内存)
- 字符串常量 1.7 - 永久代,1.8 - 堆
- MethodArea逻辑概念 - 永久代、元数据
- 新生代 = Eden + 2个suvivor区
- YGC回收之后,大多数的对象会被回收,活着的进入s0
- 再次YGC,活着的对象eden + s0 -> s1
- 再次YGC,eden + s1 -> s0
- 年龄足够 -> 老年代 (15 CMS 6)
- s区装不下 -> 老年代
- 老年代
- 顽固分子
- 老年代满了FGC Full GC
- GC Tuning (Generation)
- 尽量减少FGC
- MinorGC = YGC
- MajorGC = FGC
- 动态年龄:(不重要)
s1->s2超过50%,直接将年龄最大的放入old区
https://www.jianshu.com/p/989d3b06a49d
分配担保:(不重要) YGC期间 survivor区空间不够了 空间担保直接进入老年代 参考:https://cloud.tencent.com/developer/article/1082730
5.常见的垃圾回收器
Serial
Parallel Scavenge(PS) + Parallel Old(PO)
ParNew(Parallel Scavenge变种以配合CMS)
JDK诞生 Serial追随 提高效率,诞生了PS,为了配合CMS,诞生了PN,CMS是1.4版本后期引入,CMS是里程碑式的GC,它开启了并发回收的过程,但是CMS毛病较多,因此目前任何一个JDK版本默认是CMS 并发垃圾回收是因为无法忍受STW
- Serial 年轻代 串行回收
- PS 年轻代 并行回收
- ParNew 年轻代 配合CMS的并行回收
- SerialOld
- ParallelOld
- ConcurrentMarkSweep 老年代 并发的, 垃圾回收和应用程序同时运行,降低STW的时间(200ms) CMS问题比较多,所以现在没有一个版本默认是CMS,只能手工指定 CMS既然是MarkSweep,就一定会有碎片化的问题,碎片到达一定程度,CMS的老年代分配对象分配不下的时候,使用SerialOld 进行老年代回收 想象一下: PS + PO -> 加内存 换垃圾回收器 -> PN + CMS + SerialOld(几个小时 - 几天的STW) 几十个G的内存,单线程回收 -> G1 + FGC 几十个G -> 上T内存的服务器 ZGC 算法:三色标记 + Incremental Update
- G1(10ms) 算法:三色标记 + SATB
- ZGC (1ms) PK C++ 算法:ColoredPointers + LoadBarrier
- Shenandoah 算法:ColoredPointers + WriteBarrier
- Eplison
- PS 和 PN区别的延伸阅读: ▪https://docs.oracle.com/en/java/javase/13/gctuning/ergonomics.html#GUID-3D0BB91E-9BFF-4EBB-B523-14493A860E73
- 垃圾收集器跟内存大小的关系
- Serial 几十兆
- PS 上百兆 - 几个G
- CMS - 20G
- G1 - 上百G
- ZGC - 4T - 16T(JDK13)
常见垃圾回收器组合参数设定:(1.8)
- -XX:+UseSerialGC = Serial New (DefNew) + Serial Old
- 小型程序。默认情况下不会是这种选项,HotSpot会根据计算及配置和JDK版本自动选择收集器
- -XX:+UseParNewGC = ParNew + SerialOld
- -XX:+UseConc(urrent)MarkSweepGC = ParNew + CMS + Serial Old
- -XX:+UseParallelGC = Parallel Scavenge + Parallel Old (1.8默认) 【PS + SerialOld】
- -XX:+UseParallelOldGC = Parallel Scavenge + Parallel Old
- -XX:+UseG1GC = G1
- Linux中没找到默认GC的查看方法,而windows中会打印UseParallelGC
- java +XX:+PrintCommandLineFlags -version
- 通过GC的日志来分辨
Linux下1.8版本默认的垃圾回收器到底是什么?
Card Table 由于做YGC时,需要扫描整个OLD区,效率非常低,所以JVM设计了CardTable, 如果一个OLD区CardTable中有对象指向Y区,就将它设为Dirty,下次扫描时,只需要扫描Dirty Card 在结构上,Card Table用BitMap来实现
案例汇总
内存溢出的三种类型:
1.第一种OutOfMemoryError: PermGen space,发生这种问题的原意是程序中使用了大量的jar或class
2.第二种OutOfMemoryError: Java heap space,发生这种问题的原因是java虚拟机创建的对象太多
3.第三种OutOfMemoryError:unable to create new native thread,创建线程数量太多,占用内存过大
- -Xmn -Xms -Xmx -Xss 年轻代 最小堆 最大堆 栈空间
- -XX:+UseTLAB 使用TLAB,默认打开
- -XX:+PrintTLAB 打印TLAB的使用情况
- -XX:TLABSize 设置TLAB大小
- -XX:+DisableExplictGC System.gc()不管用 ,FGC
- -XX:+PrintGC
- -XX:+PrintGCDetails
- -XX:+PrintHeapAtGC
- -XX:+PrintGCTimeStamps
- -XX:+PrintGCApplicationConcurrentTime (低) 打印应用程序时间
- -XX:+PrintGCApplicationStoppedTime (低) 打印暂停时长
- -XX:+PrintReferenceGC (重要性低) 记录回收了多少种不同引用类型的引用
- -verbose:class 类加载详细过程
- -XX:+PrintVMOptions
- -XX:+PrintFlagsFinal -XX:+PrintFlagsInitial 必须会用
- -Xloggc:opt/log/gc.log
- -XX:MaxTenuringThreshold 升代年龄,最大值15
锁自旋次数 -XX:PreBlockSpin 热点代码检测参数-XX:CompileThreshold 逃逸分析 标量替换 … 这些不建议设置
Parallel常用参数
-XX:SurvivorRatio
- -XX:PreTenureSizeThreshold 大对象到底多大
- -XX:MaxTenuringThreshold
- -XX:+ParallelGCThreads 并行收集器的线程数,同样适用于CMS,一般设为和CPU核数相同
- -XX:+UseAdaptiveSizePolicy 自动选择各区大小比例
拓展
- -XX:MaxTenuringThreshold控制的是什么? A: 对象升入老年代的年龄 B: 老年代触发FGC时的内存垃圾比例
- 生产环境中,倾向于将最大堆内存和最小堆内存设置为:(为什么?) A: 相同 B:不同
- JDK1.8默认的垃圾回收器是: A: ParNew + CMS B: G1 C: PS + ParallelOld D: 以上都不是
- 什么是响应时间优先?
- 什么是吞吐量优先?
- ParNew和PS的区别是什么?
- ParNew和ParallelOld的区别是什么?(年代不同,算法不同)
- 长时间计算的场景应该选择:A:停顿时间 B: 吞吐量
- 大规模电商网站应该选择:A:停顿时间 B: 吞吐量
- HotSpot的垃圾收集器最常用有哪些?
- 常见的HotSpot垃圾收集器组合有哪些?
- JDK1.7 1.8 1.9的默认垃圾回收器是什么?如何查看?
- 所谓调优,到底是在调什么?
- 如果采用PS + ParrallelOld组合,怎么做才能让系统基本不产生FGC
- 如果采用ParNew + CMS组合,怎样做才能够让系统基本不产生FGC
1.加大JVM内存
2.加大Young的比例
3.提高Y-O的年龄
4.提高S区比例
5.避免代码内存泄漏 - G1是否分代?G1垃圾回收器会产生FGC吗?
- 如果G1产生FGC,你应该做什么?
- 扩内存
- 提高CPU性能(回收的快,业务逻辑产生对象的速度固定,垃圾回收越快,内存空间越大)
- 降低MixedGC触发的阈值,让MixedGC提早发生(默认是45%)
- 问:生产环境中能够随随便便的dump吗? 小堆影响不大,大堆会有服务暂停或卡顿(加live可以缓解),dump前会有FGC
问:常见的OOM问题有哪些? 栈 堆 MethodArea 直接内存
参考资料
https://blogs.oracle.com/jonthecollector]([https://blogs.oracle.com/jonthecollector/our-collectors)[/our-collectors](https://blogs.oracle.com/jonthecollector/our-collectors%29[/our-collectors)]([https://blogs.oracle.com/jonthecollector/our-collectors](https://blogs.oracle.com/jonthecollector/our-collectors))
- https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html
- http://java.sun.com/javase/technologies/hotspot/vmoptions.jsp
- JVM调优参考文档:https://docs.oracle.com/en/java/javase/13/gctuning/introduction-garbage-collection-tuning.html#GUID-8A443184-7E07-4B71-9777-4F12947C8184
- https://www.cnblogs.com/nxlhero/p/11660854.html 在线排查工具
- https://www.jianshu.com/p/507f7e0cc3a3 arthas常用命令
- Arthas手册:
- 启动arthas java -jar arthas-boot.jar
- 绑定java进程
- dashboard命令观察系统整体情况
- help 查看帮助
- help xx 查看具体命令帮助
- jmap命令参考:https://www.jianshu.com/p/507f7e0cc3a3
- jmap -heap pid
- jmap -histo pid
- jmap -clstats pid
