(1)除了释放不再被引用的对象,垃圾收集器还要处理 堆碎块 。请求分配新对象时可能不得不增大堆空间的大小,虽然可以使用的空闲空间是足够的,但是堆中没有没有连续的空间放得下新对象。可能会导致虚拟机产生不必要的”内存不足“错误。
(2)使用垃圾收集堆,有一个潜在的缺陷就是加大程序的负担, 可能影响程序的性能 。因为虚拟机需要追踪哪些对象被正在执行的程序引用,还要动态释放垃圾对象。
(3)程序可以调用System.gc()建议jvm去收集垃圾, 但是不能为垃圾回收机制指定某个对象是不是垃圾。即便调用了 gc() , 并不会马上进行垃圾回收 , 甚至不一定会执行垃圾回收 。所有的内存分配和回收权限都在jvm,不在开发人员手里。
可以试试:
public class RubbishRelease { // 类的finalize方法,可以告诉垃圾回收器应该执行的操作,该方法从Object类继承而来。 // 在从堆中永久删除对象之前,垃圾回收器调用该对象的finalize方法。 public void finalize() { System.out.println("the Object is going..."); } public static void main(String[] args) { for (int i = 0; i < 100; i++) { // 下面不断创建对象,但是这些对象都没有被引用 new RubbishRelease(); new RubbishRelease(); new RubbishRelease(); System.gc(); } System.out.println("The program is over!"); } }
运行结果:
(4)垃圾收集算法有很多,但任何垃圾收集算法都必须做两件事情。首先,它必须检测出垃圾对象 。其次,它必须 回收垃圾对象所使用的堆空间并还给程序 。
(5)区分活动对象和垃圾的两个基本方法是 引用计数 和 跟踪 。
(6) 引用计数 是垃圾收集的早期策略。在这种方法中,堆中每一个对象都有一个引用计数。一个对象被创建了,并且指向该对象的引用被分配给一个变量,这个对象的引用计数被置为1。当任何其他变量被赋值为对这个对象的引用时,计数加1。当一个对象的引用超过了生存期或者被设置一个新的值时,对象的引用计数减1。任何引用计数为0的对象可以被当作垃圾收集。当一个对象被垃圾收集的时候,它引用的任何对象计数值减1。这种方法的好处是,引用计数收集器可以很快地执行,交织在程序的运行之中。这个特性对于程序不能被长时间打断的实时环境很有利。坏处就是,引用计数无法检测出循环(即两个或者更多的对象互相引用)。
(7) 跟踪收集器 追踪从根节点开始的对象引用图。给追踪过程中遇到对象以某种方式打上标记。追踪结束时,未被标记的对象就是无法触及的,从而被收集。基本的追踪算法被称作“ 标记并清除” ,这个名字指出垃圾收集过程的两个阶段。
(8)Java虚拟机的垃圾收集器可能有对付堆碎块的策略。标记并清除收集器通常使用的两种策略是 压缩 和 拷贝 。这两种方法都是快速地移动对象来减少堆碎块。
(9) 压缩收集器 把活动的对象越过空闲区滑动到堆的一端,在这个过程中,堆的另一端出现一个大的连续空闲区。所有被移动的对象的引用也被更新,指向新的位置。
(10) 拷贝收集器 把所有的活动的对象移动到一个新的区域。在拷贝过程中,被紧挨着布置,这样可以消除原本它们在旧区域的空隙。即空闲区。一般的拷贝收集器算法被称为“停止并拷贝”。此方案中,堆被分成两个区域,任何时候都使用一个区域。对象在同一个区域中分配直到被耗尽。此时,程序执行被中止,堆被遍历,遍历时遇到活动的对象被拷贝到另个区域。当停止和拷贝过程结束时,程序恢复执行。依次往复,对于指定大小的堆来说需要两倍大小的内存,由于任何时候都只使用其中的一半,这就是该方法带来的代价。
(11) 按代收集 :根据 对象的存活周期 (一次垃圾收集为一个周期)的不同将内存划分为几块。一般是把Java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用拷贝算法,只需要付出少量存活对象的拷贝成本就可以完成收集。而老年代中因为对象存活率高、没有额外空间对它进行分配担保,可以使用“标记并清除”算法。
(12) 终结方法 (finalize),这个在上面第3点也有提到:这个方法是垃圾收集器在释放对象前必须运行。这个可能存在的终结方法使得任何Java虚拟机的垃圾收集器要完成的工作更加复杂。因为终结方法可能“复活”了某些不再被引用的对象(本身或者其他对象)。
(13) 堆 中的每一个对象都有 三种状态 之一: 可触及的 、 可复活的 以及 不可触及的 。 可触及状态 好理解。关于 可复活状态 :它在从根节点开始的追踪图中不可触及,但是又可能在垃圾收集器执行某些终结方法时触及。不仅仅是那些声明了finalize方法的对象,而是所有的对象都要经过可复活状态。而 不可触及状态 标志着不但对象不再被触及,而且也不可能通过任何终结方法复活。不可触及的对象不再对程序的执行产生影响,可自由地回收它们占据的内存。
(14)对象的强,软,弱,虚引用。
强引用:如果一个对象具有强引用,垃圾回收器绝不会回收它。当内存空间不足,JVM宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会考随意回收具有强引用的对象来解决内存不足的问题。
软引用:如果一个对象具有软引用。如果内存空间足够。垃圾回收器不会回收它。如果内存不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。
弱引用:如果一个对象具有弱引用。当垃圾回收器发现只具有弱引用对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现只具有弱引用的对象。
虚引用:虚引用不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。