java垃圾回收机制

垃圾收集器系统有本身的一套方案来判断哪一个内存块是应该被回收的,哪一个是不符合要求暂不回收的。垃圾收集器在一个Java程序中的执行是自动的,不能强制执行,即便程序员能明确地判断出有一块内存已经无用了,是应该回收的,程序员也不能强制垃圾收集器回收该内存块。程序员惟一能作的就是经过调用System. gc 方法来"建议"执行垃圾收集器,但其是否能够执行,何时执行却都是不可知的。这也是垃圾收集器的最主要的缺点。固然相对于它给程序员带来的巨大方便性而言,这个缺点是瑕不掩瑜的。程序员

 

垃圾收集器的主要特色有: 
web

1.垃圾收集器的工做目标是回收已经无用的对象的内存空间,从而避免内存渗漏体的产生,节省内存资源,避免程序代码的崩溃。
算法

2.垃圾收集器判断一个对象的内存空间是否无用的标准是:若是该对象不能再被程序中任何一个"活动的部分"所引用,此时咱们就说,该对象的内存空间已经无用。所谓"活动的部分",是指程序中某部分参与程序的调用,正在执行过程当中,还没有执行完毕。
网络

当一个方法执行完毕,其中的局部变量就会超出使用范围,此时能够被看成垃圾收集,但之后每当该方法再次被调用时,其中的局部变量便会被从新建立。函数

3.垃圾收集器线程虽然是做为低优先级的线程运行,但在系统可用内存量太低的时候,它可能会突发地执行来挽救内存资源。固然其执行与否也是不可预知的。    
4.垃圾收集器不能够被强制执行,但程序员能够经过调用System. gc方法来建议执行垃圾收集器。    
5.不能保证一个无用的对象必定会被垃圾收集器收集,也不能保证垃圾收集器在一段Java语言代码中必定会执行。所以在程序执行过程当中被分配出去的内存空间可能会一直保留到该程序执行完毕,除非该空间被从新分配或被其余方法回收。因而可知,彻底完全地根绝内存渗漏体的产生也是不可能的。可是请不要忘记,Java的垃圾收集器毕竟使程序员从手工回收内存空间的繁重工做中解脱了出来。设想一个程序员要用C或C++来编写一段10万行语句的代码,那么他必定会充分体会到Java的垃圾收集器的优势!    
6.一样没有办法预知在一组均符合垃圾收集器收集标准的对象中,哪个会被首先收集。    
7.循环引用对象不会影响其被垃圾收集器收集。    性能

8.能够经过将对象的引用变量(reference variables,即句柄handles)初始化为null值,来暗示垃圾收集器来收集该对象。但此时,若是该对象链接有事件监听器(典型的 AWT组件),那它仍是不能够被收集。因此在设一个引用变量为null值以前,应注意该引用变量指向的对象是否被监听,如有,要首先除去监听器,而后才能够赋空值。    
9.每个对象都有一个finalize( )方法,这个方法是从Object类继承来的。    
10.finalize( )方法用来回收内存之外的系统资源,就像是文件处理器和网络链接器。该方法的调用顺序和用来调用该方法的对象的建立顺序是无关的。换句话说,书写程序时该方法的顺序和方法的实际调用顺序是不相干的。请注意这只是finalize( )方法的特色。    
11.每一个对象只能调用finalize( )方法一次。若是在finalize( )方法执行时产生异常(exception),则该对象仍能够被垃圾收集器收集。    
12.垃圾收集器跟踪每个对象,收集那些不可到达的对象(即该对象没有被程序的任何"活的部分"所调用),回收其占有的内存空间。但在进行垃圾收集的时候,垃圾收集器会调用finalize( )方法,经过让其余对象知道它的存在,而使不可到达的对象再次"复苏"为可到达的对象。既然每一个对象只能调用一次finalize( )方法,因此每一个对象也只可能"复苏"一次。    
13.finalize( )方法能够明确地被调用,但它却不能进行垃圾收集。    
14.finalize( )方法能够被重载(overload),但只有具有初始的finalize( )方法特色的方法才能够被垃圾收集器调用。    
15.子类的finalize( )方法能够明确地调用父类的finalize( )方法,做为该子类对象的最后一次适当的操做。但Java编译器却不认为这是一次覆盖操做(overriding),因此也不会对其调用进行检查。    
16.当finalize( )方法还没有被调用时,System. runFinalization( )方法能够用来调用finalize( )方法,并实现相同的效果,对无用对象进行垃圾收集。    
17.当一个方法执行完毕,其中的局部变量就会超出使用范围,此时能够被看成垃圾收集,但之后每当该方法再次被调用时,其中的局部变量便会被从新建立。    
18.Java语言使用了一种"标记交换区的垃圾收集算法"。该算法会遍历程序中每个对象的句柄,为被引用的对象作标记,而后回收还没有作标记的对象。所谓遍历能够简单地理解为"检查每个"。   
19.Java语言容许程序员为任何方法添加finalize( )方法,该方法会在垃圾收集器交换回收对象以前被调用。但不要过度依赖该方法对系统资源进行回收和再利用,由于该方法调用后的执行结果是不可预知的。
编码


总之,在Java语言中,判断一块内存空间是否符合垃圾收集器收集标准的标准只有两个:    
1.给对象赋予了空值null,如下再没有调用过。    
2.给对象赋予了新值,既从新分配了内存空间。    
最后再次提醒一下,一块内存空间符合了垃圾收集器的收集标准,并不意味着这块内存空间就必定会被垃圾收集器收集。

spa


Java语言创建了垃圾收集机制,用以跟踪正在使用的对象和发现并回收再也不使用(引用)的对象。该机制能够有效防范动态内存分配中可能发生的两个危险:因内存垃圾过多而引起的内存耗尽,以及不恰当的内存释放所形成的内存非法引用。 
 
  垃圾收集算法的核心思想是:对虚拟机可用内存空间,即堆空间中的对象进行识别,若是对象正在被引用,那么称其为存活对象,反之,若是对象再也不被引用,则为垃圾对象,能够回收其占据的空间,用于再分配。垃圾收集算法的选择和垃圾收集系统参数的合理调节直接影响着系统性能,所以须要开发人员作比较深刻的了解。 
 
  2.触发主GC(Garbage Collector)的条件  
  JVM进行次GC的频率很高,但由于这种GC占用时间极短,因此对系统产生的影响不大。更值得关注的是主GC的触发条件,由于它对系统影响很明显。总的来讲,有两个条件会触发主GC: 
 
  ①当应用程序空闲时,即没有应用线程在运行时,GC会被调用。由于GC在优先级最低的线程中进行,因此当应用忙时,GC线程就不会被调用,但如下条件除外。 
 
  ②Java堆内存不足时,GC会被调用。当应用线程在运行,并在运行过程当中建立新对象,若这时内存空间不足,JVM就会强制地调用GC线程,以便回收内存用于新的分配。若GC一次以后仍不能知足内存分配的要求,JVM会再进行两次GC做进一步的尝试,若仍没法知足要求,则 JVM将报“out of memory”的错误,Java应用将中止。 
 
  因为是否进行主GC由JVM根据系统环境决定,而系统环境在不断的变化当中,因此主GC的运行具备不肯定性,没法预计它什么时候必然出现,但能够肯定的是对一个长期运行的应用来讲,其主GC是反复进行的。  
  3.减小GC开销的措施 
 
  根据上述GC的机制,程序的运行会直接影响系统环境的变化,从而影响GC的触发。若不针对GC的特色进行设计和编码,就会出现内存驻留等一系列负面影响。为了不这些影响,基本的原则就是尽量地减小垃圾和减小GC过程当中的开销。具体措施包括如下几个方面: 
 
  (1)不要显式调用System.gc() 
 
  此函数建议JVM进行主GC,虽然只是建议而非必定,但不少状况下它会触发主GC,从而增长主GC的频率,也即增长了间歇性停顿的次数。  
  (2)尽可能减小临时对象的使用 
 
  临时对象在跳出函数调用后,会成为垃圾,少用临时变量就至关于减小了垃圾的产生,从而延长了出现上述第二个触发条件出现的时间,减小了主GC的机会。  
  (3)对象不用时最好显式置为Null 
 
  通常而言,为Null的对象都会被做为垃圾处理,因此将不用的对象显式地设为Null,有利于GC收集器断定垃圾,从而提升了GC的效率。 
 
  (4)尽可能使用StringBuffer,而不用String来累加字符串(详见blog另外一篇文章Java中String与StringBuffer) 
 
  因为String是固定长的字符串对象,累加String对象时,并不是在一个String对象中扩增,而是从新建立新的String对象,如Str5=Str1+Str2+Str3+Str4,这条语句执行过程当中会产生多个垃圾对象,由于对次做“+”操做时都必须建立新的String对象,但这些过渡对象对系统来讲是没有实际意义的,只会增长更多的垃圾。避免这种状况能够改用StringBuffer来累加字符串,因StringBuffer是可变长的,它在原有基础上进行扩增,不会产生中间对象。  
  (5)能用基本类型如Int,Long,就不用Integer,Long对象   基本类型变量占用的内存资源比相应对象占用的少得多,若是没有必要,最好使用基本变量。 
 
  (6)尽可能少用静态对象变量 
 
  静态变量属于全局变量,不会被GC回收,它们会一直占用内存。 
 
  (7)分散对象建立或删除的时间  
  集中在短期内大量建立新对象,特别是大对象,会致使忽然须要大量内存,JVM在面临这种状况时,只能进行主GC,以回收内存或整合内存碎片,从而增长主GC的频率。集中删除对象,道理也是同样的。它使得忽然出现了大量的垃圾对象,空闲空间必然减小,从而大大增长了下一次建立新对象时强制主GC的机会。  
5.Java 内存泄漏 
 
  因为采用了垃圾回收机制,任何不可达对象(对象再也不被引用)均可以由垃圾收集线程回收。所以一般说的Java 内存泄漏实际上是指无心识的、非故意的对象引用,或者无心识的对象保持。无心识的对象引用是指代码的开发人员原本已经对对象使用完毕,却由于编码的错误而意外地保存了对该对象的引用(这个引用的存在并非编码人员的主观意愿),从而使得该对象一直没法被垃圾回收器回收掉,这种原本觉得能够释放掉的却最终未能被释放的空间能够认为是被“泄漏了”。  
  考虑下面的程序,在ObjStack类中,使用push和pop方法来管理堆栈中的对象。两个方法中的索引(index)用于指示堆栈中下一个可用位置。push方法存储对新对象的引用并增长索引值,而pop方法减少索引值并返回堆栈最上面的元素。在main方法中,建立了容量为64的栈,并64次调用push方法向它添加对象,此时index的值为64,随后又32次调用pop方法,则index的值变为32,出栈意味着在堆栈中的空间应该被收集。但事实上,pop方法只是减少了索引值,堆栈仍然保持着对那些对象的引用。故32个无用对象不会被GC回收,形成了内存渗漏。
线程


经过以上对垃圾收集器特色的了解,你应该能够明确垃圾收集器的做用,和垃圾收集器判断一块内存空间是否无用的标准。简单地说,当你为一个对象赋值为null而且从新定向了该对象的引用者,此时该对象就符合垃圾收集器的收集标准。    
判断一个对象是否符合垃圾收集器的收集标准,这是SUN公司程序员认证考试中垃圾收集器部分的重要考点(能够说,这是惟一的考点)。因此,考生在一段给定的代码中,应该可以判断出哪一个对象符合垃圾收集器收集的标准,哪一个不符合。下面结合几种认证考试中可能出现的题型来具体讲解:    
Object obj = new Object ( )     
咱们知道,obj为Object的一个句柄。当出现new关键字时,就给新建的对象分配内存空间,而obj的值就是新分配的内存空间的首地址,即该对象的值(请特别注意,对象的值和对象的内容是不一样含义的两个概念:对象的值就是指其内存块的首地址,即对象的句柄;而对象的内容则是其具体的内存块)。此时若是有 obj = null; 则obj指向的内存块此时就无用了,由于下面再没有调用该变量了。    
请再看如下三种认证考试时可能出现的题型:    
程序段1:    
1.fobj = new Object ( )     
2.fobj. Method ( )     
3.fobj = new Object ( )     
4.fobj. Method ( )     
问:这段代码中,第几行的fobj 符合垃圾收集器的收集标准?    
答:第3行。由于第3行的fobj被赋了新值,产生了一个新的对象,即换了一块新的内存空间,也至关于为第1行中的fobj赋了null值。这种类型的题在认证0考试中是最简单的。    
程序段2:    
1.Object sobj = new Object ( )     
2.Object sobj = null    

3.Object sobj = new Object ( )     
4.sobj = new Object ( )     
问:这段代码中,第几行的内存空间符合垃圾收集器的收集标准?    
答:第1行和第3行。由于第2行为sobj赋值为null,因此在此第1行的sobj符合垃圾收集器的收集标准。而第4行至关于为sobj赋值为null,因此在此第3行的sobj也符合垃圾收集器的收集标准。    
若是有一个对象的句柄a,且你把a做为某个构造器的参数,
即 new Constructor ( a )的时候,即便你给a赋值为null,a也不符合垃圾收集器的收集标准。直到由上面构造器构造的新对象被赋空值时,a才能够被垃圾收集器收集。    
程序段3:    
1.Object aobj = new Object ( )     
2.Object bobj = new Object ( )     
3.Object cobj = new Object ( )     
4.aobj = bobj;    
5.aobj = cobj;    
6.cobj = null;    
7.aobj = null;    
问:这段代码中,第几行的内存空间符合垃圾收集器的收集标准?    
答:第4行,第7行。注意这类题型是认证考试中可能遇到的最难题型了。    
行1-3分别建立了Object类的三个对象:aobj,bobj,cobj    
行4:此时对象aobj的句柄指向bobj,aobj原来指向的对象没有了引用,因此符合。 (原帖这里说的有错)       
行7:对象cobj符合了垃圾收集器的收集标准,由于cobj的句柄指向单一的地址空间。在第6行的时候,cobj已经被赋值为null,但由cobj同时还指向了aobj(第5行),因此此时cobj并不符合垃圾收集器的收集标准。而在第7行,aobj所指向的地址空间也被赋予了空值null,这就说明了,由cobj所指向的地址空间已经被彻底地赋予了空值。因此此时cobj最终符合了垃圾收集器的收集标准。 设计

相关文章
相关标签/搜索