java面试题网

普通会员

162

帖子

12

回复

171

积分

楼主
发表于 2018-05-25 14:40:08 | 查看: 5256| 回复: 1

垃圾回收算法概述

JAVA 并没有给我们提供明确的代码来标注一块内存并将其回收。或许你会说,我们可以将相关对象设为 null 或者用 System.gc()。然而,后者将会严重影响代码的性能,因为一般每一次显式的调用 system.gc() 都会停止所有响应,去检查内存中是否有可回收的对象。这会对程序的正常运行造成极大的威胁。另外,调用该方法并不能保证 JVM 立即进行垃圾回收,仅仅是通知 JVM 要进行垃圾回收了,具体回收与否完全由 JVM 决定。这样做是费力不讨好。

    1、追踪回收算法(tracing collector) 

    从根结点开始遍历对象的应用图。同时标记遍历到的对象。遍历完成后,没有被标记的对象就是目前未被引用,可以被回收。

    2、压缩回收算法(Compacting Collector) 

    把堆中活动的对象集中移动到堆的一端,就会在堆的另一端流出很大的空闲区域。这种处理简化了消除碎片的工作,但可能带来性能的损失。

    3、复制回收算法(Coping Collector) 

把堆均分成两个大小相同的区域,只使用其中的一个区域,直到该区域消耗完。此时垃圾回收器终端程序的执行,通过遍历把所有活动的对象复制到另一个区域,复制过程中它们是紧挨着布置的,这样也可以达到消除内存碎片的目的。复制结束后程序会继续运行,直到该区域被用完。 

但是,这种方法有两个缺陷:

对于指定大小的堆,需要两倍大小的内存空间,

需要中断正在执行的程序,降低了执行效率

    4、按代回收算法(Generational Collector) 

    为什么要按代进行回收?这是因为不同对象生命周期不同,每次回收都要遍历所有存活对象,对于整个堆内存进行回收无疑浪费了大量时间,对症下药可以提高垃圾回收的效率。主要思路是:把堆分成若搞个子堆,每个子堆视为一代,算法在运行的过程中优先收集“年幼”的对象,如果某个对象经过多次回收仍然“存活”,就移动到高一级的堆,减少对其扫描次数。


文章来自www.wityx.com,转载请注明出处!原文地址http://www.wityx.com/post/423_1_1.html


java面试题交流群:327440556

普通会员

162

帖子

12

回复

171

积分
板凳
发表于 2018-06-01 18:03:34

算法一:引用计数法。

            这个方法是最经典点的一种方法。具体是对于对象设置一个引用计数器,每增加一个变量对它的引用,引用计数器就会加1,没减少一个变量的引用,

            引用计数器就会减1,只有当对象的引用计数器变成0时,该对象才会被回收。可见这个算法很简单,但是简单往往会存在很多问题,这里我列举最明显的两个问题,

            一是采用这种方法后,每次在增加变量引用和减少引用时都要进行加法或减法操作,如果频繁操作对象的话,在一定程度上增加的系统的消耗。

            二是这种方法无法处理循环引用的情况。再解释下什么是循环引用,假设有两个对象 A和B,A中引用了B对象,并且B中也引用了A对象,

                     那么这时两个对象的引用计数器都不为0,但是由于存在相互引用导致无法垃圾回收A和 B,导致内存泄漏。

算法二:标记清除法。

            这个方法是将垃圾回收分成了两个阶段:标记阶段和清除阶段。

            在标记阶段,通过跟对象,标记所有从跟节点开始的可达的对象,那么未标记的对象就是未被引用的垃圾对象。

            在清除阶段,清除掉所以的未被标记的对象。

            这个方法的缺点是,垃圾回收后可能存在大量的磁盘碎片,准确的说是内存碎片。因为对象所占用的地址空间是固定的。对于这个算法还有改进的算法,就是我后面要说的算法三。

算法三:标记压缩清除法(Java中老年代采用)。

            在算法二的基础上做了一个改进,可以说这个算法分为三个阶段:标记阶段,压缩阶段,清除阶段。标记阶段和清除阶段不变,只不过增加了一个压缩阶段,就是在做完标记阶段后,

            将这些标记过的对象集中放到一起,确定开始和结束地址,比如全部放到开始处,这样再去清除,将不会产生磁盘碎片。但是我们也要注意到几个问题,压缩阶段占用了系统的消耗,

            并且如果标记对象过多的话,损耗可能会很大,在标记对象相对较少的时候,效率较高。

算法四:复制算法(Java中新生代采用)。

           核心思想是将内存空间分成两块,同一时刻只使用其中的一块,在垃圾回收时将正在使用的内存中的存活的对象复制到未使用的内存中,然后清除正在使用的内存块中所有的对象,

           然后把未使用的内存块变成正在使用的内存块,把原来使用的内存块变成未使用的内存块。很明显如果存活对象较多的话,算法效率会比较差,并且这样会使内存的空间折半,但是这种方法也不会产生内存碎片。

算法五:分代法(Java堆采用)。

          主要思想是根据对象的生命周期长短特点将其进行分块,根据每块内存区间的特点,使用不同的回收算法,从而提高垃圾回收的效率。

          比如Java虚拟机中的堆就采用了这种方法分成了新生代和老年代。然后对于不同的代采用不同的垃圾回收算法。

          新生代使用了复制算法,老年代使用了标记压缩清除算法。

算法六:分区算法。

           这种方法将整个空间划分成连续的不同的小区间,每个区间都独立使用,独立回收,好处是可以控制一次回收多少个小区间。

 

总结:各种回收算法都有各自的优缺点,没有一种算法可以完全替代其他的算法,在具体的使用中应该结合具体的环境来选择。


您需要登录后才可以回帖 登录 | 立即注册

java面试题网www.wuliaokankan.cnjava建站系统提供技术支持V2.1 网站地图 © 2016-2018