zoukankan      html  css  js  c++  java
  • 垃圾回收机制

    一、什么是垃圾回收

      程序的运行必然需要申请内存资源,无效的对象资源如果不及时处理就会一直占有内存资源,最终将导致内存溢出,所以对内存资源的管理是非常重要了。

    1、C/C++语言的垃圾回收

      在C/C++语言中,没有自动垃圾回收机制,是通过new关键字申请内存资源,通过delete关键字释放内存资源。

      如果,程序员在某些位置没有写delete进行释放,那么申请的对象将一直占用内存资源,最终可能会导致内存溢出。

    2、Java语言的垃圾回收

      为了让程序员更专注于代码的实现,而不用过多的考虑内存释放的问题,所以,在Java语言中,有了自动的垃圾回收机制,也就是我们熟悉的GC。

      有了垃圾回收机制后,程序员只需要关心内存的申请即可,内存的释放由系统自动识别完成。

      换句话说,自动的垃圾回收的算法就会变得非常重要了,如果因为算法的不合理,导致内存资源一直没有释放,同样也可能会导致内存溢出的。

      当然,除了Java语言,C#、Python等语言也都有自动的垃圾回收机制。

    二、垃圾回收的常见算法

      自动化的管理内存资源,垃圾回收机制必须要有一套算法来进行计算,哪些是有效的对象,哪些是无效的对象,对于无效的对象就要进行回收处理。

      常见的垃圾回收算法有:引用计数法、标记清除法、标记压缩法、复制算法、分代算法等

    1、引用计数法

      引用计数是历史最悠久的一种算法,最早George E. Collins在1960的时候首次提出,50年后的今天,该算法依然被很多编程语言使用。

      1.1  原理

        假设有一个对象A,任何一个对象对A的引用,那么对象A的引用计数器+1,当引用失败时,对象A的引用计数器就-1,如果对象A的计数器的值为0,就说明对象A没有引用了,可以被回收。

      1.2  优缺点

        优点:

           实时性较高,无需等到内存不够的时候,才开始回收,运行时根据对象的计数器是否为0,就可以直接回收。

           在垃圾回收过程中,应用无需挂起。如果申请内存时,内存不足,则立刻报outofmember 错误。

           区域性,更新对象的计数器时,只是影响到该对象,不会扫描全部对象。

        缺点:

             每次对象被引用时,都需要去更新计数器,有一点时间开销。

           浪费CPU资源,即使内存够用,仍然在运行时进行计数器的统计。

           无法解决循环引用问题。(最大的缺点)

        什么是循环引用?

    2、标记清除法

      标记清除算法,是将垃圾回收分为2个阶段,分别是标记和清除。

      标记:从根节点开始标记引用的对象。

      清除:未被标记引用的对象就是垃圾对象,可以被清理。

      2.1  原理

        

      这张图代表的是程序运行期间所有对象的状态,它们的标志位全部是0(也就是未标记,以下默认0就是未标记,1为已标记),假设这会儿有效内存空间耗尽了,JVM将会停止应用程序的运行并开启GC线程,然后开始进行标记工作,按照根搜索算法,标记完以后,对象的状态如下图

        

      可以看到,按照根搜索算法,所有从root对象可达的对象就被标记为了存活的对象,此时已经完成了第一阶段标记。接下来,就要执行第二阶段清除了,那么清除完以后,剩下的对象以及对象的状态如下图所示

        

      可以看到,没有被标记的对象将会回收清除掉,而被标记的对象将会留下,并且会将标记位重新归0。接下来就不用说了,唤醒停止的程序线程,让程序继续运行即可。

      2.2  优缺点

        可以看到,标记清除算法解决了引用计数算法中的循环引用的问题,没有从root节点引用的对象都会被回收。

      同样,标记清除算法也是有缺点的:

        效率较低,标记和清除两个动作都需要遍历所有的对象,并且在GC时,需要停止应用程序,对于交互性要求比较高的应用而言这个体验是非常差的。

        通过标记清除算法清理出来的内存,碎片化较为严重,因为被回收的对象可能存在于内存的各个角落,所以清理出来的内存是不连贯的。

    3、标记压缩算法

      标记压缩算法是在标记清除算法的基础之上,做了优化改进的算法。和标记清除算法一样,也是从根节点开始,对对象的引用进行标记,在清理阶段,并不是简单的清理未标记的对象,而是将存活的对象压缩到内存的一端,然后清理边界以外的垃圾,从而解决了碎片化的问题。

      3.1  原理

        

      3.2  优缺点

        优缺点同标记清除算法,解决了标记清除算法的碎片化的问题,同时,标记压缩算法多了一步,对象移动内存位置的步骤,其效率也有有一定的影响。

    4、复制算法

      复制算法的核心就是,将原有的内存空间一分为二,每次只用其中的一块,在垃圾回收时,将正在使用的对象复制到另一个内存空间中,然后将该内存空间清空,交换两个内存的角色,完成垃圾的回收。

      如果内存中的垃圾对象较多,需要复制的对象就较少,这种情况下适合使用该方式并且效率比较高,反之,则不适合。

       

      4.1  JVM中年轻代内存空间

        1. 在GC开始的时候,对象只会存在于Eden区和名为“From”的Survivor区,Survivor区“To”是空的。

        2. 紧接着进行GC,Eden区中所有存活的对象都会被复制到“To”,而在“From”区中,仍存活的对象会根据他们的年龄值来决定去向。年龄达到一定值(年龄阈值,可以通过-XX:MaxTenuringThreshold来设置)的对象会被移动到年老代中,没有达到阈值的对象会被复制到“To”区域。

        3. 经过这次GC后,Eden区和From区已经被清空。这个时候,“From”和“To”会交换他们的角色,也就是新的“To”就是上次GC前的“From”,新的“From”就是上次GC前的“To”。不管怎样,都会保证名为To的Survivor区域是空的。

        4. GC会一直重复这样的过程,直到“To”区被填满,“To”区被填满之后,会将所有对象移动到年老代中

      4.2  优缺点

        优点:

          在垃圾对象多的情况下,效率较高

          清理后,内存无碎片

        缺点:

         在垃圾对象少的情况下,不适用,如:老年代内存

            分配的2块内存空间,在同一个时刻,只能使用一半,内存使用率较低 

    5、分代算法

      前面介绍了多种回收算法,每一种算法都有自己的优点也有缺点,谁都不能替代谁,所以根据垃圾回收对象的特点进行选择,才是明智的选择。

      分代算法其实就是这样的,根据回收对象的特点进行选择,在jvm中,年轻代适合使用复制算法,老年代适合使用标记清除或标记压缩算法。 

    1、什么是垃圾回收?
    程序的运行必然需要申请内存资源,无效的对象资源如果不及时处理就会
    直占有内存
    资源,最终将导致内存溢出,所以对内存资源的管理是非常重要了。
    1.1C/C++语言的垃圾回收
    C/C++语言中,没有自动垃圾回收机制,是通过new关键字申请内存资源,通过delete
    关键字释放内存资源。
    如果,程序员在某些位置没有写delete进行释放,那么申请的对象将
    直占用内存资源,
    最终可能会导致内存溢出。
    1.2
    Java语言的垃圾回收
    为了让程序员更专注于代码的实现,而不用过多的考虑内存释放的问题,所以,在Java
    言中,有了自动的垃圾回收机制,也就是我们熟悉的GC
    有了垃圾回收机制后,程序员只需要关心内存的申请即可,内存的释放由系统自动识别
    完成。
    换句话说,自动的垃圾回收的算法就会变得非常重要了,如果因为算法的不合理,导致
    内存资源
    直没有释放,同样也可能会导致内存溢出的。
    当然,除了Java语言,C#Python等语言也都有自动的垃圾回收机制。
    2、垃圾回收的常见算法五道
    口互联
    网架构师学院
    第2页,共30页
    自动化的管理内存资源,垃圾回收机制必须要有
    套算法来进行计算,哪些是有效的对
    象,哪些是无效的对象,对于无效的对象就要进行回收处理。
    常见的垃圾回收算法有:引用计数法、标记清除法、标记压缩法、复制算法、分代算法
    等。
    2.1、引用计数法
    引用计数是历史最悠久的
    种算法,最早George E. Collins1960的时候首次提出,50
    年后的今天,该算法依然被很多编程语言使用。
    2.1.1、原理
    假设有
    个对象A,任何
    个对象对A的引用,那么对象A的引用计数器+1,当引用失败
    时,对象A的引用计数器就
    -
    1,如果对象A的计数器的值为0,就说明对象A没有引用了,
    可以被回收。
    2.1.2、优缺点
    优点:
    实时性较高,无需等到内存不够的时候,才开始回收,运行时根据对象的计数器是否
    0,就可以直接回收。
    在垃圾回收过程中,应用无需挂起。如果申请内存时,内存不足,则立刻报
    outofmember 错误。
    区域性,更新对象的计数器时,只是影响到该对象,不会扫描全部对象。
    缺点:
    每次对象被引用时,都需要去更新计数器,有
    点时间开销。
    浪费CPU资源,即使内存够用,仍然在运行时进行计数器的统计。
    无法解决循环引用问题。(最大的缺点)
  • 相关阅读:
    Oracle 推出 ODAC for Entity Framework 和 LINQ to Entities Beta版
    Entity Framework Feature CTP 5系列文章
    MonoDroid相关资源
    MSDN杂志上的Windows Phone相关文章
    微软学Android Market推出 Web Windows Phone Marketplace
    使用 Visual Studio Agent 2010 进行负载压力测试的安装指南
    MonoMac 1.0正式发布
    Shawn Wildermuth的《Architecting WP7 》系列文章
    使用.NET Mobile API即51Degrees.mobi检测UserAgent
    MongoDB 客户端 MongoVue
  • 原文地址:https://www.cnblogs.com/szhhhh/p/12421478.html
Copyright © 2011-2022 走看看