经常使用定义
java垃圾回收
在空闲时间以不定时的方式进行垃圾回收,回收的是无任何引用的对象占据的内存空间而不是对象自己java
触发主GC(Garbage Collector)的条件
(1)当应用程序空闲时,即没有应用线程在运行时,GC会被调用。由于GC在优先级最低的线程中进行,因此当应用忙时,GC线程就不会被调用,但如下条件除外。
(2)Java堆内存不足时,GC会被调用。当应用线程在运行,并在运行过程当中建立新对象,若这时内存空间不足,JVM就会强制地调用GC线程,以便回收内存用于新的分配。若GC一次以后仍不能知足内存分配的要求,JVM会再进行两次GC做进一步的尝试,若仍没法知足要求,则 JVM将报“out of memory”的错误,Java应用将中止。算法
内存泄露
程序中动态分配内存给一些临时对象,可是对象不会被GC所回收,它始终占用内存。即被分配的对象可达但已无用。编程
内存溢出
程序运行过程当中没法申请到足够的内存而致使的一种错误。服务器
为何要有垃圾回收机制
Java语言创建了垃圾收集机制,用以跟踪正在使用的对象和发现并回收再也不使用(引用)的对象。该机制能够有效防范动态内存分配中可能发生的两个危险:因内存垃圾过多而引起的内存耗尽,以及不恰当的内存释放所形成的内存非法引用。最终达到自动释放内存空间,减轻编程的负担的目的。markdown
垃圾回收算法
引用计数法(Reference Counting Collector)
引用计数是垃圾收集的早期策略。在这种方法中,堆中每个对象都有一个引用计数。当一个对象被建立了,而且指向该对象的引用被分配给一个变量,这个对象的引用计数被设置为1。好比新建一个对象A a=new A();而后a被分配给另一个变量b,也就是b=a;那么对象a的引用计数+1。当任何其余变量被赋值为对这个对象的引用时,计数加1。当一个对象的引用超过生存期或者被设置一个新的值时,对象的引用计数减1,好比令b=c,则a的引用计数-1。任何引用计数为0的对象能够被当作垃圾收集。当一个对象被垃圾收集的时候,它引用的任何对象计数减1。在这种方法中,一个对象被垃圾收集后可能致使后续其余对象的垃圾收集行动。好比A a=new A();b=a;当b被垃圾回收之后,a的引用计数变为0,这样致使a也被垃圾回收。
优势
引用计数收集器能够很快执行,交织在程序的运行之中。这个提醒对于程序不能被长时间打断的实时环境颇有利。
缺点
引用计数没法检测出循环(即两个或者更多的对象互相引用)。循环的例子如,父对象有一个子对象的引用,子对象又反过来引用父对象。这样对象用户都不可能计数为0,就算它们已经没法被执行程序的根对象触及。还有一个坏处就是,每次引用计数的增长或者减小都带来额外的开销。多线程
标记-清除算法(Mark-Sweep)
此算法是为了解决引用计数法带来的不足问题。垃圾回收器从根集开始扫描,识别出哪些对象可达,哪些对象不可达,并用某种方式标记可达对象,例如对每一个可达对象设置一个或多个位。当扫描结束时,未被标记的对象就是没法触及的,从而能够被收集。并发
复制算法(Copying)
此算法的提出是为了克服句柄的开销和解决堆碎片的垃圾回收。算法把内存空间划为两个相等的区域,每次只使用其中一个区域。垃圾回收时,遍历当前使用区域,把正在使用中的对象复制到另一个区域中。算法每次只处理正在使用中的对象,所以复制成本比较小,同时复制过去之后还能进行相应的内存整理,不过出现“碎片”问题。固然,此算法的缺点也是很明显的,就是须要两倍内存空间。 函数
标记-整理算法(Mark-Compact)
此算法结合了“标记-清除”和“复制”两个算法的优势。但又为了解决赋值算法的缺陷,充分利用内存空间,提出了”标记-整理”算法。该算法标记阶段和”标记-清除”同样,可是在完成标记以后,它不是直接清理可回收对象,而是将存活对象都向一端移动,而后清理掉端边界之外的内存。spa
分代收集算法(Generational Collection)
分代收集算法是目前大部分JVM的垃圾收集器采用的算法。它的核心思想是根据对象存活的生命周期将内存划分为若干个不一样的区域。通常状况下将堆区划分为老年代(Tenured Generation)和新生代(Young Generation),老年代的特色是每次垃圾收集时只有少许对象须要被回收,而新生代的特色是每次垃圾回收时都有大量的对象须要被回收,那么就能够根据不一样代的特色采起最适合的收集算法。
目前大部分垃圾收集器对于新生代都采起Copying算法,由于新生代中每次垃圾回收都要回收大部分对象,也就是说须要复制的操做次数较少,可是实际中并非按照1:1的比例来划分新生代的空间的,通常来讲是将新生代划分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden空间和其中的一块Survivor空间,当进行回收时,将Eden和Survivor中还存活的对象复制到另外一块Survivor空间中,而后清理掉Eden和刚才使用过的Survivor空间。
而因为老年代的特色是每次回收都只回收少许对象,通常使用的是Mark-Compact算法。
注意,在堆区以外还有一个代就是永久代(Permanet Generation),它用来存储class类、常量、方法描述等。对永久代的回收主要回收两部份内容:废弃常量和无用的类。.net
三.典型的垃圾收集器
经常使用的垃圾回收器
目前的收集器主要有三种:串行收集器、并行收集器、并发收集器。
一. 串行收集器
使用单线程处理全部垃圾回收工做,由于无需多线程交互,因此效率比较高。可是,也没法使用多处理器的优点,因此此收集器适合单处理器机器。固然,此收集器也能够用在小数据量(100M左右)状况下的多处理器机器上。可使用-XX:+UseSerialGC打开。
二. 并行收集器
- 对年轻代进行并行垃圾回收,所以能够减小垃圾回收时间。通常在多线程多处理器机器上使用。使用-XX:+UseParallelGC.打开。并行收集器在J2SE5.0第六6更新上引入,在Java SE6.0中进行了加强–能够堆年老代进行并行收集。若是年老代不使用并发收集的话,是使用单线程进行垃圾回收,所以会制约扩展能力。使用-XX:+UseParallelOldGC打开。
- 使用-XX:ParallelGCThreads=设置并行垃圾回收的线程数。此值能够设置与机器处理器数量相等。
- 此收集器能够进行以下配置:
- 最大垃圾回收暂停:指定垃圾回收时的最长暂停时间,经过-XX:MaxGCPauseMillis=指定。为毫秒.若是指定了此值的话,堆大小和垃圾回收相关参数会进行调整以达到指定值。设定此值可能会减小应用的吞吐量。
- 吞吐量:吞吐量为垃圾回收时间与非垃圾回收时间的比值,经过-XX:GCTimeRatio=来设定,公式为1/(1+N)。例如,-XX:GCTimeRatio=19时,表示5%的时间用于垃圾回收。默认状况为99,即1%的时间用于垃圾回收。
三. 并发收集器
能够保证大部分工做都并发进行(应用不中止),垃圾回收只暂停不多的时间,此收集器适合对响应时间要求比较高的中、大规模应用。使用-XX:+UseConcMarkSweepGC打开。
1. 并发收集器主要减小年老代的暂停时间,他在应用不中止的状况下使用独立的垃圾回收线程,跟踪可达对象。在每一个年老代垃圾回收周期中,在收集初期并发收集器会对整个应用进行简短的暂停,在收集中还会再暂停一次。第二次暂停会比第一次稍长,在此过程当中多个线程同时进行垃圾回收工做。
2. 并发收集器使用处理器换来短暂的停顿时间。在一个N个处理器的系统上,并发收集部分使用K/N个可用处理器进行回收,通常状况下1<=K<=N/4。
3. 在只有一个处理器的主机上使用并发收集器,设置为incremental mode模式也可得到较短的停顿时间。
4. 浮动垃圾:因为在应用运行的同时进行垃圾回收,因此有些垃圾可能在垃圾回收进行完成时产生,这样就形成了“Floating Garbage”,这些垃圾须要在下次垃圾回收周期时才能回收掉。因此,并发收集器通常须要20%的预留空间用于这些浮动垃圾。
5. Concurrent Mode Failure:并发收集器在应用运行时进行收集,因此须要保证堆在垃圾回收的这段时间有足够的空间供程序使用,不然,垃圾回收还未完成,堆空间先满了。这种状况下将会发生“并发模式失败”,此时整个应用将会暂停,进行垃圾回收。
6. 启动并发收集器:由于并发收集在应用运行时进行收集,因此必须保证收集完成以前有足够的内存空间供程序使用,不然会出现“Concurrent Mode Failure”。经过设置-XX:CMSInitiatingOccupancyFraction=指定还有多少剩余堆时开始执行并发收集
四. 小结
- 串行处理器:
–适用状况:数据量比较小(100M左右);单处理器下而且对响应时间无要求的应用。
–缺点:只能用于小型应用 - 并行处理器:
–适用状况:“对吞吐量有高要求”,多CPU、对应用响应时间无要求的中、大型应用。举例:后台处理、科学计算。
–缺点:应用响应时间可能较长 - 并发处理器:
–适用状况:“对响应时间有高要求”,多CPU、对应用响应时间有较高要求的中、大型应用。举例:Web服务器/应用服务器、电信交换、集成开发环境。
如何影响java垃圾回收
一般咱们在开发中没法控制JVM的垃圾回收机制,可是能够经过编程的手段来影响垃圾回收,目的是让对象符合垃圾回收条件。
1.将无用对象赋值为null
2.从新为引用变量赋值
例如:
Person p = new Person("aaa"); p = new Person("bbb");
这样,new Person(“aaa”)这个对象就是垃圾了—-符合垃圾回收条件了。
3.让相互联系的对象称为“岛”对象
Person p1 = new Person("aaa"); Person p2 = new Person("bbb"); Person p3 = new Person("ccc"); p1=p2; p2=p3; p3=p1; p1=null; p2=null; p3=null;
在没有对p一、p二、p3置null以前,它们之间是一种三角恋关系。分别置null,三角恋关系依然存在,可是三个变量不在使用它们了。三个Person对象就组成了一个孤岛,最后死在堆上—-被垃圾回收掉。
4.强制的垃圾回收System.gc()
System.gc() Runtime.getRuntime().gc()
上面的方法用于显式的通知JVM能够进行一次垃圾回收,但真正垃圾回收机制具体在什么时间点开始进行垃圾回收是不可预料的;惟一能保证的是当你内存在极少的状况,垃圾回收器在程序抛出OutofMemaryException以前运行一次。
finalize()方法
在JVM垃圾回收器收集一个对象以前,通常要求程序调用适当的方法释放资源,但在没有明确释放资源的状况下,Java提供了缺省机制来终止该对象心释放资源,这个方法就是finalize()。它的原型为:
protected void finalize() throws Throwable
finalize()方法的理解:
1.finalize()方法是Object中的方法。
2.finalize()方法会在对象被垃圾回收以前被垃圾回收器调用一次,这是Java语言的一种机制。
3.finalize()方法在任何对象上最多只会被垃圾回收器调用一次。
在finalize()方法返回以后,对象消失,垃圾收集开始执行。原型中的throws Throwable表示它能够抛出任何类型的异常。之因此要使用finalize(),是存在着垃圾回收器不能处理的特殊状况。假定你的对象(并不是使用new方法)得到了一块“特殊”的内存区域,因为垃圾回收器只知道那些显示地经由new分配的内存空间,因此它不知道该如何释放这块“特殊”的内存区域,那么这个时候java容许在类中定义一个由finalize()方法。
finalize()方法使用陷阱:
1.垃圾回收器没法保证垃圾对象能被回收,所以,finalize()方法也没法保证运行。建议不要重写finalize()方法,即便重写,也不要在finalize()方法中写关键的代码。
2.finalize()方法中能够把本身传递个别的对象,这样就不是垃圾了,避免了被回收。可是当下次这个对象又符合垃圾回收的时候,finalize()方法不会被调用第二次了,而是直接被清理掉了。
开发中经常使用的减小GC开销的措施
(1)不要显式调用System.gc()
此函数建议JVM进行主GC,虽然只是建议而非必定,但不少状况下它会触发主GC,从而增长主GC的频率,也即增长了间歇性停顿的次数。这里特别须要说明的是,在代码中显示的调用System.gc(),并不必定可以进行GC,这个咱们能够经过finalize()方法进行验证,即主动调用System.gc(),并不必定每次都调用finalize()方法。finalize()方法的特征是在对象被回收以前, 首先调用finalize()方法。
(2)尽可能减小临时对象的使用
临时对象在跳出函数调用后,会成为垃圾,少用临时变量就至关于减小了垃圾的产生,从而延长了出现上述第二个触发条件出现的时间,减小了主GC的机会。
(3)对象不用时最好显式置为Null
通常而言,为Null的对象都会被做为垃圾处理,因此将不用的对象显式地设为Null,有利于GC收集器断定垃圾,从而提升了GC的效率。
(4)尽可能使用StringBuffer,而不用String来累加字符串
因为String是固定长的字符串对象,累加String对象时,并不是在一个String对象中扩增,而是从新建立新的String对象,如 Str5=Str1+Str2+Str3+Str4,这条语句执行过程当中会产生多个垃圾对象,由于对次做“+”操做时都必须建立新的String对象,但这些过渡对象对系统来讲是没有实际意义的,只会增长更多的垃圾。避免这种状况能够改用StringBuffer来累加字符串,因StringBuffer 是可变长的,它在原有基础上进行扩增,不会产生中间对象。
(5)能用基本类型如Int,Long,就不用Integer,Long对象
基本类型变量占用的内存资源比相应对象占用的少得多,若是没有必要,最好使用基本变量。什么状况下须要使用Integer?
(6)尽可能少用静态对象变量
静态变量属于全局变量,不会被GC回收,它们会一直占用内存。
(7)分散对象建立或删除的时间
集中在短期内大量建立新对象,特别是大对象,会致使忽然须要大量内存,JVM在面临这种状况时,只能进行主GC,以回收内存或整合内存碎片, 从而增长主GC的频率。集中删除对象,道理也是同样的。它使得忽然出现了大量的垃圾对象,空闲空间必然减小,从而大大增长了下一次建立新对象时强制主GC 的机会。
参考连接:
http://blog.csdn.net/zsuguangh/article/details/6429592
http://lavasoft.blog.51cto.com/62575/112126/