GC需要完成的3件事情:
哪些内存需要回收?
什么时候回收?
如何回收?
上一部分中提到的程序计数器,虚拟机栈,本地方法栈3个区域随线程而生,随线程而灭。
对象已死吗?
在堆里存放着Java世界中几乎所有的对象实例,垃圾收集器在对堆进行回收前,第一件事情就是确定这些对象中哪些还“存活”着,哪些已经“死去”(即不可能再被任何途径使用的对象)
引用计数算法
给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻,计算器为0的对象就是不可能再被使用的。
主流Java虚拟机里面没有选用引用计数算法来管理内存,主要是因为它很难解决对象之间相互循环引用的问题。
可达性分析算法
![](https://img.haomeiwen.com/i8917733/9a543668cc74b410.png)
在Java语言中,可作为GC Roots的对象包括下面几种:
虚拟机栈(栈帧中的本地变量表)中引用的对象
方法区中类静态属性引用的对象
方法区中常量引用的对象
本地方法中JNI(即一般说的Native方法)引用的对象
垃圾收集算法
标记-清除算法
![](https://img.haomeiwen.com/i8917733/878200fabb63e9cd.png)
表情和清除两个过程效率都不高;另外,标记清楚后会产生大量不连续的内存碎片,空间碎片太多可能会导致以后程序在程序运行过程中需要分配较大对象时,无法找到足够的连续内存而不得不提前触发另一次垃圾回收动作。
复制算法
![](https://img.haomeiwen.com/i8917733/e830f999ef2d35a8.png)
将内存划分为大小相等的两块,每次只使用其中的一块,当这一块的内存用完了,就将还存活的对象复制到另外一块上面,然后把已使用过的空间一次清理掉。这样使得每次都是对整个半区进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况。问题在于,内存缩小为原来的一半,代价太大。
现在的商业虚拟机都采用了这种手机算法来回收新生代,不需要按照1:1来护法分内存空间,因为新生代中的对象98%是“朝生夕死”,可将内存分为一块较大的Eden空间和两块较小的Survivor空间。每次使用Eden和其中一块Survivor。回收时,将Eden和Survivor中还存活着的对象一次性复制到另一块Survivor空间上,最后清理掉Eden和刚才用过的Survivor空间。这样,只有10%的内存会被“浪费”。
标记-整理算法
![](https://img.haomeiwen.com/i8917733/b1fb7854ebf0ef7b.png)
复制收集算法不适合老年代的算法,因为存活率比较高。
”标记-整理算法“和”标记-清楚算法“一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界意外的内存。
分代收集算法
根据对象存活周期的不同将内存划分为几块。一般是把Java堆分为新生代和老生代。在新生代中,每次垃圾手机都发现有大批对象死去,那就选用复制算法。而老生代中,因为对象存活率高,没有额外空间对它进行分配担保,就使用“标记-清理”或者“标记-整理”来进行回收。
HotSpot算法的实现
垃圾收集器
![](https://img.haomeiwen.com/i8917733/0f9399f7acf28939.png)
Serial收集器
![](https://img.haomeiwen.com/i8917733/8828c33d10cf1138.png)
"Stop The World",它进行垃圾收集时,必须暂停其他所有的工作线程,知道它收集结束。
优点:简单而高效(与其他收集器的单线程比),对于限定单个CPU的环境来说,Serial收集器由于没有线程交互的开销,专心做垃圾收集自然可以获得最高的单线程收集效率。
ParNew收集器
![](https://img.haomeiwen.com/i8917733/17a00db0d02b3d57.png)
ParNew收集器其实就是Serial收集器的多线程版本。
ParNew是许多运行在Server模式下的虚拟机中首选的新生代收集器,其中有一个与性能无关的重要原因是,除了Serial收集器外,只有它能与CMS收集器配合工作。
并行:指多条垃圾收集线程并行工作,但此时用户线程任然处于等待状态。
并发:指用户线程与垃圾收集线程同时执行(但不一定是并行的,可能会交替执行),用户程序在继续运行,而垃圾收集程序运行于另一个CPU上。
Parallel Scavenge收集器
Parallel Scavenge和Parallel的却别在于,后者关注的是尽可能缩短垃圾收集时用户线程的停顿时间,而前者的目标则是达到一个可控制的吞吐量。
停顿时间越短就越适合需要与用户交互的程序,良好的响应速度能提升用户体验,而高吞吐量则可以高效率地利用CPU时间,尽快完成程序的运算任务,主要适合在后台运算而不需要太多交互的任务。
Parallel Scavenge收集器提供了两个参数用于精确控制吞吐量,分别是控制最大垃圾收集停顿时间的-XX:MaxGCPauseMillis参数以及直接设置吞吐量大小的-XX:GCTimeRatio参数。
Parallel Scavenge收集器也经常称为“吞吐量优先”收集器。
-XX:+UseAdaptiveSizePolicy,这是一个开关参数,当这个参数打开后,就不需要手工指定细节参数了,虚拟机会根据当前系统的运行情况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间或者最大吞吐量,这种调节方式成为GC自适应的调节策略。
Serial Old收集器
是Serial收集器的老年代版本,同样是一个单线程收集器。
在Server模式下,主要还有两大用途:
在JDK1.5以及之气那的版本中与Parallel Scavenge收集器搭配使用
作为CMS收集器的后背预案,在并发收集发生Concurrent Mode Failure时使用。
网友评论