Java GC
java垃圾回收器自动回收,没有任何引用链接的就是垃圾
如何确定垃圾
引用计数法:记录引用计数,为0就是垃圾,但是无法解决循环引用找不到垃圾问题
根可达算法:没有同GC roots链接的,都是垃圾
垃圾清除算法
Mark-Sweep 标记清除,算法相对简单,两遍扫描(扫描一遍存活的,再扫描一遍未存活的)效率偏低,容易产生碎片,所以不适合eden区。存活对象比较多的情况下效率较高
Copying 拷贝,分为两块内存,将有用的拷贝到另一块内存。空间浪费,移动复制对象,需要调整对对象的引用。适用于对象存活比较少的情况,只扫描一次,效率提高,没有碎片。适用于eden区
Mark-Compact 标记压缩,算法相对复杂,两遍扫描,需要移动对象,效率偏低。不会产生碎片,方便对象分配,不会产生内存减半
GC算法基础概念
因为根对象有可能已经到了old区,所以由于做YGC时,需要扫描整个OLD区,效率非常低,所以JVM设计了CardTable, 如果一个OLD区CardTable中有对象指向Y区,就将它设为Dirty,下次扫描时,只需要扫描Dirty Card
在结构上,Card Table用BitMap来实现
堆内存逻辑分区(适用于分代的垃圾回收器,从ZGC开始不再分代了)
新生代,老年代1:2
1、新new出来的对象,首先尝试栈上分配,栈上分配不下放到eden区。
2、经过一次垃圾回收后,仍然存活,放到survivor区。再经过一次垃圾回收后,仍然存活,放到另一个survivor区,同时eden区存活的对象也放到这个survivor区里。
3、当来回再survivor区的次数超过15次后,进入老年代。(对象mark word 分代记录4字节,锁两个自己 )
新生代、老年代是堆内,永久代是堆外
永久代(1.7)Perm Generation/ 元数据区(1.8) Metaspace 是Method Area 方法区的实现
- 永久代 存放 元数据 - Class
- 永久代必须指定大小限制 ,元数据区可以设置,也可以不设置,无上限(受限于物理内存)
- 字符串常量 1.7放于永久代,1.8 放于堆中
- MethodArea逻辑概念 - 1.7叫Perm Generation, 永久代、 1.8叫MetaSpace 放于元数据区(放置除了Class之外的其他信息,jit)
分代及相关参数设置
-Xmn:设置年轻代大小
-Xms:最小内存,-Xmx:最大内存
栈上分配和线程本地分配
栈上分配(不需要垃圾回收,弹栈就消失了,效率较高)
无逃逸:逃逸分析其实就是分析一个方法中创建出来的‘对象’的作用域是否限制于本方法内。
public void test(){ new hello(); }//没有逃逸
Hello h; public void test(){ h = new hello(); }//逃逸了
支持标量替换:调用时不反回对象类型,而是返回一个基础数据类型
-XX:-DoEscapeAnalysis -XX:-EliminateAllocations -XX:-UseTLAB 关闭逃逸分析,标量替换,线程本地分配
对象何时进入老年代
记忆点,除CMS是6,其他的都是15
动态年龄:当survivor区在互相拷贝时,发现该survivor区+eden,超过自身的一半了,将年龄最大的放到老年代中
分配担保:YGC期间 如果仍然发现survivor区空间不够了 空间担保直接进入老年代
对象分配总结
1、实例化之后,看能否放到栈中,可以放到栈中,弹出结束
2、不能放到栈中,看对象大小,如果大,放到老年代,Full/major GC结束
3、如果不大,看能否放到线程本地分配中,最终放到eden区
4、GC算法执行,未存活,minor/young GC结束。存活的s1,s2区来回复制,年龄大放到OLD区,
全部的垃圾回收器
垃圾回收器的成长历程
JDK诞生 Serial就有了。为了提高效率,诞生了PS。为了配合CMS,诞生了PN,CMS是1.4版本后期引入,CMS是里程碑式的GC,它开启了并发回收的过程,但是CMS毛病较多,因此目前任何一个JDK版本默认是CMS
并发垃圾回收是因为无法忍受STW
垃圾回收器的成长与服务器内存大小有很大关系:
1、serial 几十兆 可以处理
2、PS 上百兆~几个G 可以处理
3、CMS 20G
4、G1 上百G
5、ZGC 4T
Serial 单线程
Parallel 多线程
Young
Serial,ParNew,Parallel Scavenge
Old
CMS,Serial Old,Parallel Old
常见组合
Serial + Serial Old
Parallel Scavenge+Parallel Old
ParNew+CMS
垃圾回收器详述
串行
Serial:年轻代 串行回收
a stop-the-world,copying collector which uses a single GC thread。
在需要垃圾回收时,并且处于safe point 可以执行STW时,进行STW。GC开始
设计解释:
当时的虚拟机内存比较小,单线程STW的时间不会很长,单机而现在的内存比之前要比之前大的多了,STW时间变得很长,不满足需求了Serial Old:老年代 串行回收
a stop-the-world,mark-sweep-compact collector that uses a single GC thread
并行
生产环境未作任何调优,默认就是ps+po
parallel Scavenge:PS 年轻代 并行回收
a stop-the-world,copying collector which uses multiple GC threadsparallel old:PO 老年代 并行回收
a compacting collector that uses multiple GC threads
并发垃圾回收,垃圾回收和应用程序同时运行,降低STW时间
ParNew paralle new,Paralle Scanvenge的增强,年轻代 配合CMS的并行回收,默认线程数为CPU核数
- a stop-the-world,copying collector whitch uses multiple GC threads
- It differs from “parrallel Scavenge” in that it has enhancements that make it usable with CMS
- For example,“ParNew” does the synchronization needed so that it can run during the concurrent phases of CMS
(ParNew和Paralle Scanvenge比较)
PN响应时间优先,为了配合CMS。PS吞吐量优先
CMS :ConcurrentMarkSweep 老年代 并发的,垃圾回收和应用程序同时运行,降低STW的时间(200MS)
CMS有一些问题,所以没有任何版本默认是CMS的,只能手工指定。CMS是MarkSweep,必然会导致碎片,当碎片到一定程度,CMS老年代分配对象分配不下时,使用serialold进行老年代串行回收。
会产生浮动垃圾
解决方案:降低触发CMS的阈值,保持老年代有足够的空间
-XX:CMSInitiatingOccupancyFraction 92% 降低百分比六个阶段
预处理阶段
初始标记:STW,标记出根对象。因为只标记跟对象,所以很快
并发标记:标记垃圾,浪费80%GC时间,同应用程序一同运行
重新标记:STW,并发处理中产生了新的垃圾,或者有一些垃圾重新被使用,不再是垃圾。因为大部分垃圾在并发标记时处理过了,所以也很快。
并发清理:将产生的垃圾清除掉,但是同时也会产生新的垃圾。也就是浮动垃圾,这种浮动垃圾只能等待下一个垃圾回收进行处理
整理阶段
三色标记,并发标记时的算法
CMS使用的是三色标记+Incremental Update算法
G1使用的是三色标记+snapshot at the begining (SATB)算法
ZGC: Colored Pointers+写屏障
Shenandoah :Colored Pointers+读屏障