概述
在JVM中,我们的堆和元空间都是线程共享的,所以我们可能存在当堆空间的容量不支持存放新的对象的时候,就会触发垃圾回收机制,那么,回收的时候是根据什么情况回收的呢。
怎么判断该对象是灭亡状态
引用计数法:
概述:对象中添加一个引用计数器,如果引用计数器为0则表示没有其它地方在引用它。如果有一个地方引用就+1,引用失效时就-1。
缺点:实际上在大部分Java虚拟机中并没有采用这种算法,因为它会带来一个致命的问题—对象循环依赖。对象A指向B,对象B反过来指向A,此时它们的引用计数器都不为0,但它们俩实际上已经没有意义因为没有任何地方指向它们。
可达性分析算法:(主流的使用这个)
概述:根节点是一个称为GC Roots的对象,从这个对象开始向下搜索并作标记,遍历完这棵树过后,未被标记的对象就会判断“已死”,即为可被回收的对象。
哪些可以作为根节点的GCroot对象呢:
1.虚拟机栈中引用的对象。
2.方法区中类静态属性实体引用的对象。
3.方法区中常量引用的对象。
4.本地方法栈中JNI引用的对象
回收算法
1.标记清除算法
如图所示:假设红色是可回收对象,绿色是回收之后的对象的引用空间,当被GC回收之后,我们可以发现空间是不连续的,而且每个对象的大小不相等,那我就会发生一种情况,当我们创建一个对象,假设所有的空间大小都不支持存放的话,就会提前触发GC。
缺点:
1.标记和清除的过长较长,效率不高。
2.产生大量不连续的碎片,空间利用率不高,可能提前触发GC。
2.标记整理算法
如图所示:假设红色是可回收对象,绿色是回收之后的对象的引用空间,当我们触发GC的时候,首先我们会把可用的对象整理在一起,回收的对象整理在一起,然后再把回收的对象回收。对象的引用地址可能发生变化,短暂造成STW。
优点:空间利用率高,没有碎片化。
缺点:STW,短暂卡顿现象。
3.标记复制算法
概述:红色代表可回收对象,绿色代表可用对象,当我们触发GC的时候,我们会把可用对象整体复制到to区,然后直接清除整块的from区。
优点:效率高,空间连续性
缺点:以空间换时间,也会造成STW
4.0 分代算法:
概述:Java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。而老年代中因为对象存活率高、没有额外空间对它进行分配担保,使用标记整理算法来进行回收。
垃圾回收器
新生代收集器:Serial、ParNew、Parallel Scavenge
老年代收集器:CMS、Serial Old、Parallel Old
整堆收集器: G1
1.Serial收集器是最基本的、发展历史最悠久的收集器。
参数控制:-XX:+UseSerialGC 串行收集器
特点:单线程、简单高效(与其他收集器的单线程相比),对于限定单个CPU的环境来说,Serial收集器由于没有线程交互的开销,专心做垃圾收集自然可以获得最高的单线程手机效率。收集器进行垃圾回收时,必须暂停其他所有的工作线程,直到它结束(Stop The World)。
2.ParNew收集器其实就是Serial收集器的多线程版本。
参数控制:-XX:+UseParNewGC ParNew收集器
特点:多线程、ParNew收集器默认开启的收集线程数与CPU的数量相同。
3.Parallel Scavenge 收集器与吞吐量关系密切,故也称为吞吐量优先收集器。
参数控制:-XX:+UseParallelGC 使用Parallel收集器+ 老年代串行
特点:属于新生代收集器也是采用复制算法的收集器,又是并行的多线程收集器(与ParNew收集器类似)
4.Serial Old 收集器 Serial Old是Serial收集器的老年代版本。
参数控制:-XX:+UseParallelOldGC使用Parallel收集器+ 老年代并行
特点:同样是单线程收集器,采用标记-整理算法。
5.Parallel Old 收集器是Parallel Scavenge收集器的老年代版本。
特点:多线程,采用标记-整理算法
6.CMS收集器一种以获取最短回收停顿时间为目标的收集器。
核心理念:减少stw时间。使用标记整理的话,stw时间会变长,违背设计理念。虽然有碎片化问题,但是用户不会阻塞
特点:基于标记-清除算法实现。并发收集、低停顿。
收集过程:
初始标记 :标记GCroot直接关联的对象,也会stw,速度很快。
并发标记:初始标记结束后,线程就会启动,同时多线程并发标记与初始标记相关的链 (用户线程与GC线程一起运行)
重新标记:修正标记,当并发标记时,用户线程也在启动,会导致GCroot的引用链发生变动。 也会stw
并发清除:使用标记清除算法清除,
图片来源于网络:
CMS收集器的优点:
1.STW时间短
2.用户线程和GC同时运行
CMS收集器的缺点:
1.对CPU资源非常敏感。
2.由于在使用并发清除的时候,GC线程和用户线程是同时运行的,所以可能禅城无法处理的浮动垃圾,可能出现Concurrent Model Failure失败而导致另一次Full GC的产生。
3.因为采用标记-清除算法所以会存在空间碎片的问题,导致大对象无法分配空间,不得不提前触发一次Full GC
并发清除时,碎片化很多,会发生什么问题:
直接触发FullGC ,采用串行老年化GC清理整个堆内存垃圾(标记整理算法),导致所有用户线程等到。
G1回收器:一款面向服务端应用的垃圾收集器
为什么推出G1回收器:传统的回收器都分为新生代和老年代,当有大对象的时候会直接进入老年代,而老年代回收的频率比较少,导致占用内存。
怎么判断一个对象是对象:
如果一个对象的大小大于一个块一半的大小,就可以称之为大对象,直接放在H区。
特点:
并行与并发:G1能充分利用多CPU、多核环境下的硬件优势,使用多个CPU来缩短Stop-The-World停顿时间。部分收集器原本需要停顿Java线程来执行GC动作,G1收集器仍然可以通过并发的方式让Java程序继续运行。
分代收集:G1能够独自管理整个Java堆,并且采用不同的方式去处理新创建的对象和已经存活了一段时间、熬过多次GC的旧对象以获取更好的收集效果。
空间整合:G1运作期间不会产生空间碎片,收集后能提供规整的可用内存。
可预测的停顿:G1除了追求低停顿外,还能建立可预测的停顿时间模型。能让使用者明确指定在一个长度为M毫秒的时间段内,消耗在垃圾收集上的时间不得超过N毫秒。(自己有一个)
G1为什么能建立可预测的停顿时间模型?
因为它有计划的避免在整个Java堆中进行全区域的垃圾收集。G1跟踪各个Region里面的垃圾堆积的大小,在后台维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的Region。这样就保证了在有限的时间内可以获取尽可能高的收集效率
G1回收器的收集主要有三个部分:
1.年轻代GC
2.新生代和并发标记过程
3.混合回收
初始标记:仅标记GC Roots能直接到的对象,并且修改TAMS(Next Top at Mark Start)的值,让下一阶段用户程序并发运行时,能在正确可用的Region中创建新对象。(需要线程停顿,但耗时很短。)
并发标记:从GC Roots开始对堆中对象进行可达性分析,找出存活对象。(耗时较长,但可与用户程序并发执行)
最终标记:为了修正在并发标记期间因用户程序执行而导致标记产生变化的那一部分标记记录。且对象的变化记录在线程Remembered Set Logs里面,把Remembered Set Logs里面的数据合并到Remembered Set中。(需要线程停顿,但可并行执行。)
筛选回收:对各个Region的回收价值和成本进行排序,根据用户所期望的GC停顿时间来制定回收计划。(可并发执行)