GC的基础知识
1.什么是垃圾,怎么申请?怎么回收
C语言申请内存:malloc /free
C++: new /delete
Java: new ?(java中没有回收方法)
自动内存回收,编程上简单,系统不容易出错,手动释放内存,容易出两种类型的问题:
- 忘记回收
- 多次回收
没有任何引用指向的一个对象或者多个对象(循环引用)
2.如何定位垃圾
- 引用计数(计算哪些对象是否被引用了,但若三个对象相互引用,但又没有对象引用这个循环,那么就存在内存泄露《即存在垃圾》)
- Root Searching 根查询法:通过根变量(正在运行的线程栈变量=main方法中的局部变量、静态变量、常量池、JNI指针以及clazz文件对象)去往下推,再通过这些根变量的引用计数法,没引用的就是垃圾
3.定位后常见的垃圾回收算法
- 标记清除 :找到后标记,然后清除。缺点:清除后内存位置不连续 产生碎片
- 拷贝算法 :分配一份同样大小的内存B,每次将A中已使用的内存拷贝到B,然后清除A,下次变化时,再从B拷贝到A。优缺点:没有碎片,太浪费空间
- 标记压缩 :标记清除的垃圾的同时,将后面已使用内存剪切到已清除垃圾的位置,同时保证内存的连续。优缺点:没有碎片,但效率偏低
4.JVM内存分代模型(用的是分代垃圾回收算法= Generation)
- 是部分垃圾回收器使用的模型
- 由三代组成,新生代 + 老年代 + 永久代(JDK1.7)或者 新生代 + 老年代 + 元数据区(JDK1.8)
- 永久代/元数据区:不由堆内存管,只被操作系统限制
- 永久代 元数据 - 存Class对象
- 永久代必须指定大小限制 ,元数据可以设置,也可以不设置,无上限(元数据上线仅限于物理内存)
- 字符串常量 :1.7 存放再永久代,1.8 - 存放到堆中
- 方法区其实时一个逻辑概念 :1.7存永久代、1.8存元数据
- 堆内存可以分为25%新生代+75%老年代
- 新生代 = Eden(80%) + 2个suvivor区(各占10%):当我们new对象时,默认先存伊甸(Eden)中,若存不下,直接存入老年代中
- 进行一次YGC(youngGC=年轻GC)的自动回收之后,大多数(90%)的对象会被回收,活着的会通过拷贝算法 直接copy进入suvivor01区(copy效率高),再整个清除Eden区供下次使用
- 再次YGC,eden中活着的对象 + suvivor01活着的 ---->进入到suvivor02,清除Eden与suvivor01
- 再次YGC,eden中活着的对象 + suvivor02活着的 ----->进入到 suvivor01,清除Eden与suvivor02
- 多次YGC后,即年龄足够 -> 进入老年代 (15 CMS 6)
- suvivor区装不下 -> 直接进入老年代
- 老年代
- 装的都是顽固分子
- 老年代满了,触发一次FGC (Full GC)= YGC (新生代)+ oldGC(老年代),这种全内存垃圾回收的算法比较复杂
- GC Tuning (GC调优,以分代回收算法为例)
- 可以尽量减少FGC:需要看具体业务,有些可能要满足一个月一次FGC(这个效果就相当可以了),有些1小时或者1天一次也行
- MinorGC = YGC
- MajorGC = FGC
- 小结:例如一台内存为12G的机器,我们给新生代分配3G,老年代9G,那么YGC做的就是3G的垃圾回收,FGC就是12G的垃圾回收
5.常见的垃圾回收器
- Serial 年轻代 串行回收(程序与GC串行),例如:程序运行中,当内存满了,Serial会暂停所有正在运行的线程,触发GC,GC后再继续运行程序(这也就是为什么我们运行的应用跑了一阵时间后会出现卡顿现象,当触发GC时,程序是暂停的)
- PS(Paraller Scavenge) 年轻代 并行回收:可以多个线程同时进行GC
- ParNew 年轻代 并行回收,与PS基本一致,但ParNew可以配合CMS的
- SerialOld :可以理解为可以在老年代使用的Serial
- ParallelOld:可以理解为可以在老年代使用的PS
- CMS(ConcurrentMarkSweep) 老年代 并发的,很复杂但效率高, 垃圾回收和应用程序同时运行,降低STW的时间(停顿时间降到200ms)
- G1(停顿时间降到10ms):非分代模型的垃圾回收器,2020年后可能会用的比较多,现在大多数公司还是用的分代模型
- ZGC (停顿时间降到1ms) STW的效率可以PK C++了都:非分代模型的垃圾回收器
- Shenandoah:非分代模型的垃圾回收器
- Eplison:debug时用的一个垃圾回收器,一般为空的
- 最早的分代模型组合的就是Serial+SerialOld
- JDK1.8默认的垃圾回收(大多公司):PS + ParallelOld
- 目前来看比较好的组合:ParNew + CMS