1.为什么要使用垃圾收集?
字面是说是“垃圾收集”,更精确点就是“内存回收”,在一个对象不再被程序引用时,它所占用的堆空间就可以回收,以便分配给新对象使用。而且除了释放不再被引用的对象外,垃
圾收集器还要处理堆碎块(堆碎块是在正常的程序运行时产生的),因为新的对象分配了空间,不再被引用的对象被释放,所以堆内存的空闲位置介于活对的对象之间,而请求分配新对象时可能不得不增大堆空间,因为虽然总的空闲空间是够的,但堆中没有连续的空闲空间放得下新对象。
上面是垃圾收集的作用,其好处在于:1)提高了工作效率,在一个没有垃圾收集机制的语言下编程,程序员还要花时间来解决难以捉摸的内存问题;
2)帮助程序保持完整性,因为程序员不可能因为失误错误地释放内存而导致jvm崩溃;
2.垃圾收集器怎么知道对象不再被引用?
垃圾检测通常是通过建立一个根对象的集合并且检查从这些根对象开始的可触及性来实现。可触及就是正在执行的程序可以访问到的根对象和某个对象之间存在引用路径。
区分活动对象和垃圾的两个基本方法是引用计数和跟踪。
3.引用计数收集器是怎么判定对象为垃圾的?
引用计数的原理是:堆中第一个对象都有一个引用计数,当有一个对象被创建时,这个对象的引用计数被置为1。当有其他变量被赋值为这个对象的引用时,计数加1;当这个对象的引用超过了生存期或被设置为新的值,计数减1;当计数为0时,对象被认为是垃圾,将被回收;
这种方法的早已过时,主要缺陷是:他无法检测出循环引用(即两个或多个对象互相引用),例如:父对象有一个对子对象的引用,子对象又反过来引用父对象,这时计数永远不可能为0。
4.跟踪收集器又是怎么一回事?
跟踪就是对对象设置标记,通过一个从根结点开始的对象引用图追踪对象,在这个过程中对遇到的对象打上标记,当追踪结束时没有被打上标记的就是无法触及的,从而可以被回收。
5.怎么处理堆碎块?
一般处理堆碎块有以下几种收集器:
1.压缩收集器:把活动的对象都推到堆的一端,从而使另一端出瑞一个大的连续空闲区,所有被移动的对象的引用也被更新,指向新的位置;为了简便,更新被移动的对象引用时通过一个间接引用层,因为不直接引用堆中的对象而对象的引用实际上指向一个对象句柄表的话不用改变引用指向的位置,但损失了部分性能。
2.拷贝收集器:把所有的活动的对象移动到一个新的区域,在这个过程中,活动对象被紧挨着布置从而消除原本它们在旧区域的空隙。一般的拷贝收集算法是——停止并拷贝,在这个方法中堆被分为两个区域,任何时候都只使用其的一个区域。对象在同一个区域分配,直到这个区域满了为止;这时程序中止,堆被遍历,遍历到的活动对象被拷贝到另外一个区域,完成后程序继续执行,如此循环;这种方法的代价是:对于指定大小的堆来说需两倍大小的内存。
下图是“停止并拷贝”算法的垃圾收集堆的图形描述:第一张图中,堆的下半部分没有被使用,上半部分被对象零散的填充;2图是上半部分逐渐被填充,3图中上半部分被填满,这时程序中止,垃圾收集器开始工作,从根结点开始追踪活动对象图,当遇到活动对象时就拷贝到堆的下半部分,每一个都紧挨着,如4图;如此打循环:
3.按代收集的收集器:
简单的停止拷贝收集器的缺点在于:第一次收集,所有的活动对象都要被拷贝,而程序中一般都有以下特点:
1)大多数程序创建的大部分对象都具有很短的生命期;
2)大多数程序都创建一些具有非常长生命周期的对象;
简单的停止拷贝收集器每次都把那些生命周期长的对象来回拷,浪费了时间;按代收集器通过把对象按照寿命来分组,更多的收集那些年幼的对象,当一个年幼对象经过几次收集后仍然存活,那就将它提升为寿命更高的一代,从而对这个对象的收集频率就减少了,帮命越长的收集频率就越小;
4.自适应收集器:看这个收集器的名字,顾名思义:根据不同的情况选择不同的垃圾收集技术;
6.什么是火车算法?
由于垃圾收集时一般会停止程序,如果停止的时间长得让用户注意到而不满意,那就说明这种算法存在破坏性,为了减少这种破坏性而采用渐进式收集算法(每次回收一部分),通常渐进式收集器都是按代收集器,而火车算法就是为了在成熟空间空间(最高寿的那个年龄层)提供限定时间的渐进收集。
火车算法把成熟对象空间划分为固定长度的内存块,算法每次在一个块中单独执行,每一个块属于一个集合,块就是“车厢”,集合就是“火车”,成熟对象空间就是“火车站”;就如车厢一样,块在一个集合中有序的;集合也被排序,就像火车站中的火车按轨道排列一样,如下图:
对象从更年轻的年龄层的子堆中进入成熟对象空间都会被附加到任何已存在的火车中或者为他们专门创建一列或多列火车。
每一次执行火车算法的时候,它不是收集最小数字的火车中的最小数字车厢,就是收集整列最小数字的火车,算法首先检查指向最小数字火车中任何车厢的引用,如果不存在任何来自最小数字火车以外的引用指向它内部包含的对象,那么整个火车都是垃圾,回收整个火车;如果不都是垃圾,那么将注意力放在火车中最小数字车厢上,首先把所有被最小数字车厢外部的车厢引用的对象转移到其他车厢去,之后任何保留在车厢内的对象都是没有引用的,可以被回收;
而保证整列火车中没有循环的数据结构的关键是算法如何移动对象:
1)如果对象被成熟对象空间的其他火车引用,对象不被转移到引用它的那列火车中去;
2)然后转移过后的对象被扫描,查找对原车厢的引用,发现的任何被外用的对象都被转移到引用它的火车中去;
3)新被转移的对象也被扫描,这个过程不断重复,直到没有任何来自其他火车的引用指向正被收集的车厢;
4)如果接收对象的火车没有空间了,那么算法会创建新的车厢,并附加到那列火车的尾部;
7.终结
java中一个对象可以拥有终结方法:这个方法是垃圾收集器在释放对象前必须运行的。这个方法使垃圾收集器要做的工作更加复杂,因为垃圾收集器必须检查它所发现的不再被引用的对象是否存在终结方法——finalize();还得记住一点:是垃圾收集器运行对象的终结方法。
8.GC中对象的六种可触及状态
1.强可触及:对象可以从根结点不通过任何引用对象搜索到
2.软可触及:对象不是强可触及的,但是可以从根结点开始通过一个或多个(未被清除的)软引用对象触及
3.弱可触及:对象既不是强可触及也不是软可触及的,但是从根结点开始可以通过一个或多个弱引用对象触及
4.可复活的:对象既不是强可触及、软可触及,也不是弱可触及,但是仍然可能通过执行某些终结方法复活到这几种状态之一
5.影子可触及:不上以上任何可触及状态,也不能通过终结方法复活,并且它可以从根结点开始通过一个或多个影子引用对象触及(影子引用不会被垃圾收集器清除,由程序明确地清除)
6不可触及:就是已经准备回收的状态