垃圾回收GC(Garbage Collection)简单的一句话介绍:回收无任何引用的对象占据的内存。html
这个java虚拟机实现的,全部咱们java程序员,new了一个对象后,就不用管何时释放这个对象的内存,后面java虚拟机会自动帮咱们释放。java
(咱们知道java中基本的数据类型是在栈里面,因此编译器是知道该数据的存活时间,能够自动释放,可是java的对象都是在堆里存储,是经过new出来了,没有像c++中的局部对象,因此编译器也就不知道存储的数据在堆里存活活多长时间,因此要程序来释放,c++中经过析构函数释放,而java基本经过虚拟机垃圾回收释放,不用本身写)c++
其实垃圾回收并非java的专利,不少语言都有垃圾回收机制,1960年 基于MIT的Lisp首先提出了垃圾回收的概念,用于处理C语言等不停的析构操做,而这时Java尚未出世呢!因此实际上GC并非Java的专利,GC的历史远远大于Java的历史! 程序员
其实垃圾回收机制主要作如下两件事算法
1.回收无任何引用的对象占据的内存空间网络
2.整理内存碎片,将其融合。多线程
因为建立对象和垃圾回收器释放对象,因此会出现内存碎片,碎片是分配给对象块之间的内存空洞,碎片整理是将对象让其一个挨着一个,推到堆的一边,这样整理出来的内存就能够分给新建立的对象了。并发
理解GC工做机制能够帮助你写出更好的Java应用程序,咱们能够:函数
一、 排查内存溢出 二、 排查内存泄漏 三、 性能调优,排查并发瓶颈
不少人误觉得java有了垃圾回收机制以后,就不会有内存泄漏,这是错误的理解,由于java里面的对象并不是老是被垃圾回收,及对象可能不被垃圾回收,有些特殊状况,即经过建立对象之外的方式为对象分配了存储空间。这种时候,垃圾回收就不会回收,咱们可使用finalize()的方式来释放内存,这种特殊状况举例:分配内存时可能用到了相似C语言中的作法,而非java中的一般作法,这种状况主要发生在“本地方法”的状况下,本地方法是一种在java中调用非java代码的方式。性能
是一种很简单但速度很慢的垃圾回收技术,每一个对象都含有一个引用计数器,当有引用链接至该对象的时候,引用记数加1,当引用离开做用域或被置为null时,引用记数减一,虽然管理引用记数的开销不大,可是这项开销在整个程序生命周期中将持续发送,垃圾回收器会在含有所有对象的列表上遍历,当发现某个对象的引用记数为0时,就释放其占用的空间。
当咱们的代码出现下面的情形时,该算法将没法适应
a) ObjA.obj = ObjB
b) ObjB.obj - ObjA
这样的代码会产生以下引用情形 objA指向objB,而objB又指向objA,这样当其余全部的引用都消失了以后,objA和objB还有一个相互的引用,也就是说两个对象的引用计数器各为1,而实际上这两个对象都已经没有额外的引用,已是垃圾了。
缺点:若是对象之间有循环引用时,可能会出现“对象应该被回收,可是记数却不为零”的状况。
这种垃圾回收机制彷佛从未被应用到任何一种java虚拟机中。
在如今的垃圾回收器基本采用的是这种方法来寻找活的对象,这种思路的原理是,任何 “活”的对象,必定能最终追溯到其存活在堆栈或静态存储区之中的引用,这个引用链条可能会穿过数个对象层次。
因此根搜索算法以下:
从堆栈和静态存储区开始,遍历全部的引用,就能找到全部“活”的对象。对于发现的每一个引用,必须追踪他所引用的对象,若是这个对象还包含引用,就必须追踪这些引用的所引用的对象,依次递归下去,直到“根源于堆栈和静态存储区中引用”所造成的网络被所有访问为止,你所访问过的对象必须是“活”的。
这样就解决了“交互自引用”的对象组的问题---由于这种对象根本就不会被发现,所以也就被自动回收了。
算法以下图
其实根节点能够是以下:
如今的垃圾收集器基本都是使用这个方法来收集垃圾,而收集后的垃圾是经过什么算法来回收的呢?
一、 标记-清除算法
二、 复制算法
三、 标记-整理算法
四、 分代收集算法
咱们来逐一过一下
一、 标记-清除算法
标记-清除算法采用从根集合进行扫描,对存活的对象对象标记,标记完毕后,再扫描整个空间中未被标记的对象,进行回收,如上图所示。
标记-清除算法不须要进行对象的移动,而且仅对不存活的对象进行处理,在存活对象比较多的状况下极为高效,但因为标记-清除算法直接回收不存活的对象,所以会形成内存碎片!
二、 复制算法
复制算法采用从根集合扫描,并将存活对象复制到一块新的,没有使用过的空间中,这种算法当控件存活的对象比较少时,极为高效,可是带来的成本是须要一块内存交换空间用于进行对象的移动。也就是咱们前面提到的s0 s1等空间。
三、 标记-整理算法
标记-整理算法采用标记-清除算法同样的方式进行对象的标记,但在清除时不一样,在回收不存活的对象占用的空间后,会将全部的存活对象往左端空闲空间移动,并更新对应的指针。标记-整理算法是在标记-清除算法的基础上,又进行了对象的移动,所以成本更高,可是却解决了内存碎片的问题。
四、分代收集算法
当前的商业虚拟机中基本都是使用“分代收集”算法,这种算法并无什么新的思想,只是根据对象存活周期的不一样将内存划分为几块,通常是把java堆分为了新生代和老年代,在新生代中,每次垃圾收集时都发现有大批对象死去,只有少许存活,而老年代中对象存活率高,这样就能够根据各个年代的特定采用最适当的收集算法。
目前大部分垃圾收集器对于新生代都采起stop-copy算法,由于新生代中每次都要回收大量的对象,因此复制的对象少,可是实际中并非按照1:1的比例来划分新生代的空间的,通常来讲是将新生代划分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden空间和其中的一块Survivor空间,当进行回收时,将Eden和Survivor中还存活的对象复制到另外一块Survivor空间中,而后清理掉Eden和刚才使用过的Survivor空间。
而因为老年代的特色是每次回收都只回收少许对象,通常使用的是Mark-Compact算法。
对象的内存分配,往大方向上讲就是在堆上分配,对象主要分配在新生代的Eden Space和From Space,少数状况下会直接分配在老年代。若是新生代的Eden Space和From Space的空间不足,则会发起一次GC,若是进行了GC以后,Eden Space和From Space可以容纳该对象就放在Eden Space和From Space。在GC的过程当中,会将Eden Space和From Space中的存活对象移动到To Space,而后将Eden Space和From Space进行清理。若是在清理的过程当中,To Space没法足够来存储某个对象,就会将该对象移动到老年代中。在进行了GC以后,使用的即是Eden space和To Space了,下次GC时会将存活对象复制到From Space,如此反复循环。当对象在Survivor区躲过一次GC的话,其对象年龄便会加1,默认状况下,若是对象年龄达到15岁,就会移动到老年代中。
因为对象进行了分代处理,所以垃圾回收区域、时间也不同。GC有两种类型:Scavenge GC和Full GC。
Scavenge GC
通常状况下,当新对象生成,而且在Eden申请空间失败时,就会触发Scavenge GC,对Eden区域进行GC,清除非存活对象,而且把尚且存活的对象移动到Survivor区。而后整理Survivor的两个区。这种方式的GC是对年轻代的Eden区进行,不会影响到年老代。由于大部分对象都是从Eden区开始的,同时Eden区不会分配的很大,因此Eden区的GC会频繁进行。于是,通常在这里须要使用速度快、效率高的算法,使Eden去能尽快空闲出来。
Full GC
对整个堆进行整理,包括Young、Tenured和Perm。Full GC由于须要对整个对进行回收,因此比Scavenge GC要慢,所以应该尽量减小Full GC的次数。在对JVM调优的过程当中,很大一部分工做就是对于FullGC的调节。有以下缘由可能致使Full GC:
· 年老代(Tenured)被写满
· 持久代(Perm)被写满
· System.gc()被显示调用
·上一次GC以后Heap的各域分配策略动态变化
垃圾收集算法是 内存回收的理论基础,而垃圾收集器就是内存回收的具体实现。下面介绍一下HotSpot(JDK 7)虚拟机提供的几种垃圾收集器,用户能够根据本身的需求组合出各个年代使用的收集器。
1.Serial/Serial Old
Serial/Serial Old收集器是最基本最古老的收集器,它是一个单线程收集器,而且在它进行垃圾收集时,必须暂停全部用户线程。Serial收集器是针对新生代的收集器,采用的是Copying算法,Serial Old收集器是针对老年代的收集器,采用的是Mark-Compact算法。它的优势是实现简单高效,可是缺点是会给用户带来停顿。
2.ParNew
ParNew收集器是Serial收集器的多线程版本,使用多个线程进行垃圾收集。
3.Parallel Scavenge
Parallel Scavenge收集器是一个新生代的多线程收集器(并行收集器),它在回收期间不须要暂停其余用户线程,其采用的是Copying算法,该收集器与前两个收集器有所不一样,它主要是为了达到一个可控的吞吐量。
4.Parallel Old
Parallel Old是Parallel Scavenge收集器的老年代版本(并行收集器),使用多线程和Mark-Compact算法。
5.CMS
CMS(Current Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器,它是一种并发收集器,采用的是Mark-Sweep算法。
CMS 处理过程有七个步骤:
a. 初始标记(CMS-initial-mark) ,会致使swt;
b. 并发标记(CMS-concurrent-mark),与用户线程同时运行;
c. 预清理(CMS-concurrent-preclean),与用户线程同时运行;
d. 可被终止的预清理(CMS-concurrent-abortable-preclean) 与用户线程同时运行;
e. 从新标记(CMS-remark) ,会致使swt;
f. 并发清除(CMS-concurrent-sweep),与用户线程同时运行;
g. 并发重置状态等待下次CMS的触发(CMS-concurrent-reset),与用户线程同时运行;
6.G1
G1收集器是当今收集器技术发展最前沿的成果,它是一款面向服务端应用的收集器,它能充分利用多CPU、多核环境。所以它是一款并行与并发收集器,而且它能创建可预测的停顿时间模型。
参考资料
http://jbutton.iteye.com/blog/1569746
http://www.cnblogs.com/dolphin0520/p/3783345.html
http://pengjiaheng.iteye.com/blog/524024