⑴背景
Java堆和方法区实现类所需内存是不一样的,每个方法的多分支需要的内存也可能不一样,我们只有在运行期间才能制动创建哪些对象。这部分内存分配与回收都是动态的,而垃圾回收器所关注的就是这些这部分内存。
⑵基本垃圾回收算法
①引用计数法:给每个对象添加一个引用计数器,每当有一个地方引用它时,计数器值就加1,当引用失效时,计数器就减1,任何时刻计数器为0的对象是不可能被使用的。
引用计数法简单易实现,但判定效率也很高,在大部分情况下式不错的算法。但主流Java虚拟机都没有使用引用计数器来管理内存,主要原因是他很难解决对象之间的循环引用问题
例如:ObjA.instance = ObjB,ObjB.instance = ObjA,除此之外这两个对象再无其他引用。实际上这两个对象没有别其他对象应用,但是引用计数器无法通知GC来回收它们。
②可达性分析:在Java中是使用可达性分析来判定对象是否存活。算法思路就是通过一系列称为“GC Roots”的队形作为起点,从这些节点向下搜索搜索所走的路径称为“引用链”,当一个对象
到GC Roots没有任何引用链相连时,则证明此对象是不可用的。
如图:
③标记-清除算法:标记-清楚算法分为两个阶段,首先标记处所需的回收对象,在标记完成之后统一回收所有标记的对象,后面的算法都是基于该算法进行改进。该算法有两大不足之处:一是效率低,标记与清除的两个过程效率不高;二是空间问题,标记清除之后会产生大量的不连续内存碎片,空间碎片过多会导致以后程序运行时需要分配较大对象时,无法找到足够的连续内存,而不得不提前触发一次垃圾收集动作。
如图(图片来自网络):
④复制算法:复制算法常用于新生代。将可用内存按容量划分为大小相等的两块,每次使用其中一块,当这一块用完之后,仍存活的对象赋值到另一块上,然后再把已使用过的内存空间一次清理掉。这样使得每次都是对整个搬去进行内存回收,内存分配时不需要考虑内存不连续,碎片的情况,只需要按顺序分配内存即可。但是该算法的代价是内存缩小为原来的一半。属于用空间换时间。
在新生代的对象中98%是“朝生夕死”的,所以并不需要按照1:1的比例划分空间,而是将内存分为一块较大的Eden空间和两块较小的Suvivor空间,每次使用Eden和其中一个Survivor,当回收时,将Eden和Suvivor中还存活的对象一次性地复制到另一块Survivor空间上,然后清楚掉Eden和刚才用过的Survivor空间。
⑤标记-整理:标记-整理算法常用于老年代,如果在内存中如果对象的存活率较高,使用复制算法效率就会非常低下。于是根据老年代的特点出现了“标记-整理”算法,其标记过程与“标记-清除”算法一样,后续步骤不是直接就对可回收对象进行清除,而是让所有存活的对象移动到一端,然后直接清理掉边界以外的内存。
如图(图片来自网络):
⑥分代收集算法:根据对象存活周期分为新生代和老年代,然后根据各自年代特点使用不同算法。在新生代中,每次垃圾收集时发现有大批对象死去,存货量较小,这时候使用复制算法就非常合适。在老年代中,对象的存活率较高,使用复制算法显然不划算,这时候就选择采用“”“标记-整理”算法去实现内存回收。
⑶垃圾收集器
HotSpot虚拟机垃圾收集器(图片来自网络)
①Serial收集器:该收集器是一个单线程的收集器,只会使用一个CPU或一条收集线程去完成垃圾收集,而且在进行垃圾收集时,其他工作线程都必须暂停。
②ParNew收集器:ParNew是Serial收集器的多线程版本。
ParNew收集器是Server模式下的虚拟机中首选的新生代收集器,其中一个与性能无关但很重要的原因是,除了Serial收集器外,目前只有它能够与CMS收集器配合工作,在单CPU环境中效果不比Serial,但但当CPU非常多的环境下,ParNew的优势就体现出来了。
③ CMS收集器:以获取最短回收停顿时间为目标的收集器,CMS收集器基于“标记-清除”算法实现。
过程分为4个部分:
Ⅰ 初始标记:初始标记仅仅只是标记GC Roots所能够关联到的对象(可达性分析)
Ⅱ 并发标记:根搜索(GC Roots Tracing)算法基本原理是:GCRoot对象作为起始点(根)。如果从根到某个对象是可达的,则该对象称为“可达对象”(存活对象,不可回收对象)。
Ⅲ 重新标记:重新标记阶段是为了修正并发标记期间因用户程序运作而导致标记产生变动的那一部分对象的标记记录。(边打扫卫生边仍纸屑)
Ⅳ 并发清除:并发将可回收内存全部回收。
其中需要“Stop The World”的有Ⅰ,Ⅲ过程
CMS缺点:
Ⅰ.CPU资源非常敏感,在面向并发程序时,CMS虽然不会导致用户线程停顿,但仍然会因为并发标记或并发清除占用了部分线程,导致应用程序变慢,总吞吐量降低。
Ⅱ.CMS无法处理浮动垃圾,由于CMS并发清理阶段用户线程仍在运行着,伴随程序运行,自然还会有新的垃圾不断产生,这一部分垃圾出现在标记标记过程之后,CMS无法在当次集中处理它们,只能留到下一次GC再处理.
Ⅲ.CMS基于“标记-清除”算法,该算法会产生大量的空间碎片,会出现老年代空间剩余,但无法找到连续的空间去分配当前对象,就必须提前触发一次Full GC.
④G1收集器:G1收集器是目前前沿收集器技术之一,到了JDK7u4才达到了商用程度
G1收集器所具备的特点:
Ⅰ.并发与并行:G1具有CMS一样多线程操作能力,G1能利用多CPU、多核环境下的硬件优势,使用多个CPU来缩短Stop-The-World停顿时间,部分其他收集器原本需要停顿Java线程执行的GC动作,G1收集器仍然能够通过并发方式让Java线程继续执行。
Ⅱ.分代收集:G1不需要其他收集器配合就能够独立管理整个GC堆,但他能够使用不同的方式去处理新创建对象和已存活一段时间对象、熬过多次的旧对象都有自己的处理方法。
Ⅲ.空间整合:CMS基于“标记-清除”,缺点是出现许多不连续内存,而G1则基于“标记-整理”,从局部(两个Region)上看基于“复制”算法,G1运行期间不会产生大量空间碎片,收集后能提供规整的可用内存空间,在分配大对象时不会由于找不到可用的连续内存而出发下一次GC。
Ⅳ.可预测停顿:G1与CMS不同,G1除了追求低停顿以外,还能建立可预测的停顿时间模型。
更详细:http://blog.csdn.net/renfufei/article/details/41897113