JVM-垃圾回收器_01
- 垃圾回收器(Garbage Collectors)
- 没有引用指向的对象就是垃圾
根搜索算法(ROOT Searching)
在方法里面有一些局部变量,我们new出一个对象(根对象),这个对象会产生一些引用形成新的对象,新的对象也可能产生一些引用形成新对象。
所以在运行时找到这个方法,只要能找到根对象就不是垃圾,找不到根对象的便是垃圾。
什么是根?
以下来自JVMS(Java虚拟机规范)
- JVM stack : 虚拟机栈中引用的对象
- native method stack : 本地方法栈中的对象
- runtime constant pool : 运行时常量池
- static references in method area : 静态引用的对象
- clazz
回收算法
- Mark-Sweep(标记清除):
- 将垃圾标记出来,直接清除掉。
- 缺点:碎片化,会产生碎片,当分配大对象时可能会找不到空间
- Copying (拷贝):
- 将内存一分为二,只用一半
- 缺点:会造成内存浪费
- Mark-Compact(标记压缩):
- 将垃圾标记出来后,再进行一个压缩。利于产生新对象
- 缺点:效率低
垃圾回收器的发展路线
-
随着内存越来越大的过程而演进
- 从分代算法演化到不分代算法
- 前六种:分代
- G1:概念分代,物理上不分代
- ZGC、Shenandoah:不分代
- Epsilon:什么都不干
-
serial: 几十兆
-
Parallel:几个G
-
CMS:几十个G,承上启下,开始并发回收
-
G1:上百G,逻辑分代,物理不分代
-
ZGC和Shenandoah:上T,逻辑和物理都不分代
-
Epsilon:啥也不干(调试,确认不用GC参与的程序)
在早期,会将内存的堆空间分成两个不同的年代
-
新生代,新new出来的对象
- 年轻代又分为三块:
- eden(伊甸)
- 两块survivor(幸存者)
- 由于对eden进行一次垃圾回收时会有八、九成的对象需要回收,所以当对eden进行垃圾回收的时候,只需要将不需要回收的对象复制到第一块survivor中,然后将整个eden进行垃圾回收。
- 第二次对eden垃圾回收时,由于第一块survivor中可能也存在需要回收的垃圾,所以将eden和第一块survivor中不需要回收的对象复制到第二块survivor中,然后将eden和survivor中的全部对象进行回收。(YGC)
- 之后的垃圾回收,只需要将两块survivor调换位置即可。而多次垃圾回收都没有清理掉的对象,会被存放到老年代中。
- 年轻代又分为三块:
-
老年代,经历过多次GC的顽固对象
- 只有当老年代分配不下新对象时才会进行垃圾回收,多数时候在对老年代回收时也会对年轻代进行回收,所以也被叫做Full GC(FGC)
- 不同垃圾回收器中,能进入老年代的对象的年龄不同
- ps,po:15
- CMS:6
- G1:15
-
在不同的年代里采用不同的算法
- 年轻代使用Copying算法
- 老年代使用Mark Compact或者Sweep
前六种分代的GC通常进行组合使用
- Serial开头的是单线程
- Parallel开头的是多线程
- parNew:新一代的多线程
- CMS:并发标记清除
- SerialOld:单线程工作在老年代
- ParallelOld:多线程工作在老年代
Serial Young
a stop-the-world,copying collector which uses a single GC thread
(STW,使用拷贝算法的单线程回收器)
STW:使用垃圾回收器的时候,所有工作线程停止。
Serial Old
a stop-the-world, mark-sweep-compact collector which uses a single GC thread
(STW,使用标记清除或标记压缩算法的单线程回收器)
Parallel Scavenge
a stop-the-world,copying collector which uses multiple GC threads
(STW,使用拷贝算法的多线程回收器)
Parallel Old
a stop-the-world mark-sweep-compact collector which uses multiple GC threads
(STW,使用标记清除或标记压缩算法的多线程回收器)
CMS
先进行初始标记,初始标记会STW(相较于其他分代GC的STW时间更短,因为只找根对象),再进行并发标记,但并发标记会存在标记失误,所以会进行重新标记,重新标记时会STW,最后进行并发清理。
浮动垃圾:在标记过程中,对象被引用,而标记结束后,被停止引用而产生的垃圾。这次没回收掉,下一轮再回收。
parNew
a stop-the-world,copying collector which uses multiple GC threads
(STW,使用标记清除或标记压缩算法的多线程回收器)
与parallel Scavenge的不同点:可以和CMS组合工作
常用组合
- Serial + Serial Old:已不常见
- Parallel Scavenge + Parallel Old:JDK1.8中默认使用的PS+PO组合,称为Parallel GC
- ParNew + CMS
G1
Garbage First
从G1开始不再分代,而是开始了分区(Region)清理。
ZGC和Shenandoah
两者都来源于C4。不需要进行调优,参数只有一个,号称没有任何STW。(但是贼贵)
ZGC是Oracle官方推出的,诞生于JDK11
Shenandoah是RedHat推出的,在JDK12中可以设置,主要跑在Open JDK
Epsilon
- 作测试时使用,观察垃圾产生过程。
- 不需要进行垃圾回收的程序使用
分代GC的路线
在分代模型中,当我们new一个对象时,会优先在栈上分配。在栈上分配的对象不会涉及垃圾回收器,使用结束后自动弹出。
如果不在栈上分配,会判断该对象大不大。
-
如果大,则直接分配在Old(老年代)中,等待FGC。
-
如果小,则分配在TLAB(线程本地分配缓存区,Thread Local Allocation Buffer),无论是否分配在TLAB中,最终都会分配到eden中。
- 由于会造成线程同步,资源浪费,所以会为每个线程都分配一块专属的空间,这样不会产生线程竞争,效率高。
伊甸区的对象经过GC清除后,如果清除掉就结束,没有清除掉进入s1(survivor),再经历GC后,如果仍未被清除掉,就判断年龄,年龄足够进入老年代,年龄不足进入s2。
注:如何判断new出的对象大不大?
- new出的对象大小超过了伊甸区
- 伊甸区中已经存在对象,new出的新对象再伊甸区中放不下且大于伊甸区的一半
- 通过参数指定
博主个人网站:https://wobuaini.online