垃圾回收器
什么是垃圾对象?
没有任何引用的对象。
如何发现垃圾对象?
- reference count 引用计数
- 每引用一次给计数器+1,减少一次引用进行-1。
- 无法解决的问题,循环引用。A引用B,B引用C,C引用A,但是ABC三个整体没有被其他的引用。 ABC整体应该是一个垃圾,引用计数无法发现。
- root search 根可达算法
- 从线程对象(main方法)出发搜索对象是否可达。
- 从静态变量出发。
- 常量池
-
JNI指针,c++实现的本地方法
GC Algorithms 垃圾清除算法
- Mark-Sweep (标记清除)将不可用的对象标记,然后清除。
- 优点:
- 算法实现简单
- 存活对象较多时效率较高
- 缺点
- 扫描两遍(第一次找出不可用对象并标记,第二遍遍历清除)效率较低
- 容易产生碎片
- 优点:
- copy(拷贝)将内存一份为二,将可用的对象拷贝到一块区域。
- 优点:
- 对象存活较少的情况下只扫描一遍,效率较高
- 没有碎片
- 缺点:
- 浪费空间
- 移动复制对象,需要调整对象引用
- 优点:
- Mark-Compact (标记压缩)将可用对象移动至内存的某个区域,剩下的就是大块的不可用的区域,再删除不可用的。
- 优点
- 不会产生内存碎片,方便对象分配
- 不会内存减半
- 缺点
- 扫描两次效率较低
- 需要移动对象,效率较低
- 优点
堆内存逻辑分区(不适合不分代的垃圾收集器,除Epsilon、ZGC、shenandoah之外都是逻辑分代模型,G1是逻辑分代不是物理分代,除此之外不仅逻辑分代而且还是 物理分代)
- 新生代:大量死去少量存活,采用的复制算法。eden、survivor1、survivor2默认比例是8:1
- eden:初生代
- survivor:有两块survivor区域
- 老年代:有大量存活,少量死亡,采用ms(Mark swap标记清除)或mc(Mark compact 标记压缩)算法。
- tenured:老年代或者叫永久代。
一个对象的消亡过程
- 尝试在栈上分配
- 能在栈分配必须满足:
- 线程私有小对象
- 无逃逸
- 支持标量替换(对象可以一个或多个基本类型替换)
- 能在栈分配必须满足:
对象的生命周期会随着方法的调用开始而开始,方法的调用结束而结束,对于这种对象,是不是该考虑将对象不在分配在堆空间中呢?因为一旦分配在堆空间中,当方法调用结束,没有了引用指向该对象,该对象就需要被gc回收,而如果存在大量的这种情况,对gc来说无疑是一种负担
- 进入Eden
- Eden 区默认1%的区域有个叫TLAB(Thread Local Allocation Buffer)多线程不用竞争就可以申请空间提高效率。
- Eden存活下来,进入survivor1
- survivor1存活下来进入survivor2
- survivor2存活下来进入survivor1如此往复,指导达到配置好的代数,后进入老年代。
MiniorGC/YGC: 年轻代空间耗尽触发。
MajorGC/FullGC:老年代空间不足,触发年轻代和老年代同时回收
对象何时进入老年代
- 超过-XX:MaxTenuringThreshold 指定数值
- PS 15
- CMS 5
- G1 15
- 动态年龄
- Survivor(Eden + s1 +s2)区的对象年龄从小到大进行累加,当累加到 X 年龄时的总和大于50%(可以使用-XX:TargetSurvivorRatio=? 来设置保留多少空闲空间,默认值是50),那么比X大的都会晋升到老年代
- 分配担保
在发生minor gc之前,虚拟机会检测 : 老年代最大可用的连续空间 > 新生代all对象总空间?
1、满足,minor gc是安全的,可以进行minor gc。
2、不满足,虚拟机查看HandlePromotionFailure参数:- 为true,允许担保失败,会继续检测老年代最大可用的连续空间>历次晋升到老年代对象的平均大小。若大于,将尝试进行一次minor gc,若失败,则重新进行一次full gc。
- 为false,则不允许冒险,要进行full gc(对老年代进行gc)。
常见的垃圾回收器
Serial收集器
Serial收集器是最基础、历史最悠久的收集器,曾经(在JDK 1.3.1之前)是HotSpot虚拟机新生代 收集器的唯一选择。是个单线程的垃圾回收器,这里的单线程强调的不是这个回收器是由一个核心的一个线程完成,强调的是“stop the world”,该垃圾回收线程工作时必须停掉其他工作线程。
适用场景:对于单核处理器或处理器核心数较少的环境来说,Serial收集器由于没有线程交互的开销,专心做垃圾收集自然可以获得最高的单线程收集效率。
类型:新生代回收器
ParNew收集器
是Serial收集器的多线程并行版本,ParNew收集器实质上是Serial收集器的多线程并行版本,除了同时使用多条线程进行垃圾收集之外 , 其余的行为包括Serial收集器可用的所有控制参数(例如:-XX:SurvivorRatio、-XX:PretenureSizeThreshold、-XX:HandlePromotionFailure等)、收集算法、Stop The World、对象分配规则、回收策略等都与Serial收集器完全一致但它却是不少运行在服务端模式下的HotSpot虚拟机,经管如此,尤其在jdk7之前还是有大量的服务端适用的是ParNew,重要原因就是只有它可以配合CMS使用。
类型:新生代回收器
适用场景:ParNew+CMS 第一款真正意义上支持并发的垃圾收集器,它首次 实现了让垃圾收集线程与用户线程(基本上)同时工作。
并行(Parallel):并行描述的是多条垃圾收集器线程之间的关系,说明同一时间有多条这样的线 程在协同工作,通常默认此时用户线程是处于等待状态。
并发(Concurrent):并发描述的是垃圾收集器线程与用户线程之间的关系,说明同一时间垃圾 收集器线程与用户线程都在运行。由于用户线程并未被冻结,所以程序仍然能响应服务请求,但由于 垃圾收集器线程占用了一部分系统资源,此时应用程序的处理的吞吐量将受到一定影响。
Parallel Scavenge收集器
Parallel Scavenge收集器的特点是它的关注点与其他收集器不同,CM S等收集器的关注点是尽可能 地缩短垃圾收集时用户线程的停顿时间,而Parallel Scavenge收集器的目标则是达到一个可控制的吞吐 量(Throughput)。
- -XX:MaxGCPauseMillis 控制最大垃圾收集停顿时间
- -XX:GCTimeRatio 直接设置吞吐量
适用场景:优先考虑吞吐量的应用,而不是STW时间。
类型:新生代垃圾回收器
Serial Old收集器
Serial Old是一个使用标记整理算法的单线程收集器。这个垃圾回收器主要也是提供给客户端模式下使用的,Parallel Scavenge收集器架构中本身有PS MarkSweep收集器来进行老年代收集,并非直接调用Serial Old收集器,PS MarkSweep收集器与Serial Old的实现几乎是一样的,所以在官方的许多资料中都是直接以Serial Old代替PS MarkSweep进行讲解。
适用场景:单核处理器或处理器核心数较少的环境,内存也不是特别大的场景。
类型:老年代回收器
Parallel Old收集器
Parallel Old是Parallel Scavenge收集器的老年代版本,支持多线程并发收集,基于标记-整理算法实 现。这个收集器是直到JDK 6时才开始提供的,在此之前使用Parallel Scavenge只能选择Serial Old(PS MarkSweep)其他表现性能较好的如CMS无法使用,由于Serial Old表现并不突出,所以使用Parallel Scavenge并不能获得吞吐量上的提升。
适用场景:搭配Parallel Scavenge使用
类型:老年代回收器
CMS收集器
CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。更短的停顿时间可以获的更短的响应时间。收集过程包括以下几个部分
- 初始标记(CMS initial mark)
- 并发标记(CM S concurrent mark)
- 重新标记(CM S remark)
- 并发清除(CM S concurrent sweep)
常见垃圾回收器组合参数设定:(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版本默认的垃圾回收器到底是什么?
- 1.8.0_181 默认(看不出来)Copy MarkCompact
- 1.8.0_222 默认 PS + PO