zoukankan      html  css  js  c++  java
  • 内存清除算法

    1.标记-清除

    标记 GC roots可达的对象,清理掉没有被标记的对象。


    做法:当堆中的有效内存空间被耗尽的时候,就会停止整个程序,然后进行两项工作,第一项是标记,第二项是清除。

        1.标记:遍历所有的GC Roots,然后将GC Roots可达的对象标记为存活的对象。
        2.清除:清除的过程将遍历对中所有的对象,将没有标记的对象全部清除掉。

    当程序运行期间,若可以使用的内存被耗尽的时候,GC线程就会被触发并将程序暂停,随后将依旧存活的对象标记一遍,最终再将堆中所有没被标记的对象全部清除掉,接下来便让程序恢复运行。

    标记的时候为什么有停止程序运行呢?
    假设我们刚标记完图中最右边的那个对象,暂且记为A,结果此时在程序当中又new了一个新对象B,且A对象可以到达B对象。但是由于此时A对象已经标记结束,B对象此时的标记位依然是0,因为它错过了标记阶段。因此当接下来轮到清除阶段的时候,新对象B将会被苦逼的清除掉。如此一来,不难想象结果,GC线程将会导致程序无法正常工作。上面的结果当然令人无法接受,我们刚new了一个对象,结果经过一次GC,忽然变成null了,这还怎么玩?

    缺点

         1.效率较低(递归,遍历整个堆的对象)而且在进行GC的时候,需要停止应用程序,这会导致用户体验非常差劲。
         2.清理出来的内存空间不是连续的(死亡对象都是随机出现在内存的各个角落的)。再分配数组对象的时候,寻找连续的内存空间不太好找。


    2.复制算法:

    将内存分为两块

    做法:当内存空间耗尽时,暂停程序运行,开启复制算法GC线程。将活动区间(内存)的存活对象复制到空闲的那一块内存区域,并且严格的按照内存地址依次排列,与此同时GC线程将更新存活对象的内存引用地址指向新的内存地址。 将标记为死亡对象一次清除掉。之后(活动)的那一块内存区域变为空闲,空闲的变为忙。

    缺点:

         1.浪费了一半内存
         2.如果对象的存活率很高,假设为100%存活,那么就需要将所有对象都复制一遍,并且将所有引用地址复制一遍。复制这一工作所话费的时间,在对象存活率达到一定程度时,(复制所用时间)将会变的不可忽视。

    总结:要想使用复制算法,最起码对象的存活率要非常低才行,而且最重要的是,我们必须要克服50%的内存浪费。


    3.标记-整理算法:


    做法:

         1.和标记-清除算法一样,标记出存活的对象。
         2.按照内存地址依次排列,而未被标记的内存会被清理掉。

    不难看出,标记-整理算法不仅可以弥补 标记-清除 算法中内存区要分散的缺点,也消除了复制算法中内存减半的高额代价。(但是从效率上讲,标记-整理算法要低于复制算法)


    算法总结:


    1.都是基于根搜索算法(GC Roots)的来判断一个对象是否应该被回收
    2.在GC线程开启时,或者说GC过程开始时,它们都要暂停服务。
    3. 效率

         复制算法   >   标记/整理算法   >   标记/清除算法(此处的效率只是简单的对比时间复杂度,实际情况不一定如此)。

         内存整齐度:复制算法   =   标记/整理算法   >   标记/清除算法。

         内存利用率:标记/整理算法   =   标记/清除算法   >   复制算法。

    结束语

    到此我们已经将三个算法了解清楚了,可以看出,效率上来说,复制算法是当之无愧的老大,但是却浪费了太多内存,而为了尽量兼顾上面所提到的三个指标,标记/整理算法相对来说更平滑一些,但效率上依然不尽如人意,它比复制算法多了一个标记的阶段,又比标记/清除多了一个整理内存的过程。
    难道就没有一种最优算法吗?
    当然是没有的,这个世界是公平的,任何东西都有两面性,试想一下,你怎么可能找到一个又漂亮又勤快又有钱又通情达理,性格又合适,家境也合适,身高长相等等等等都合适的女人?就算你找到了,至少有一点这个女人也肯定不满足,那就是多半不会恰巧又爱上了与LZ相似的各位苦逼猿友们。你是不是想说你比LZ强太多了,那LZ只想对你说,高富帅是不会爬在电脑前看技术文章的,0.0。
    但是古人就是给力,古人说了,找媳妇不一定要找最好的,而是要找最合适的,听完这句话,瞬间感觉世界美好了许多。
    算法也是一样的,没有最好的算法,只有最合适的算法。


    既然这三种算法都各有缺陷,高人们自然不会容许这种情况发生。因此,高人们提出可以根据对象的不同特性,使用不同的算法处理,类似于萝卜白菜各有所爱的原理。于是奇迹发生了,高人们终于找到了GC算法中的神级算法-----分代搜集算法。

    4.!分代搜集算法:

    本质 属于前三种算法的实际应用 新生代,老年代,永久代

    新生代:朝生夕灭,存活时间短。eg:某一个方法的局部变量,循环内的临时变量等等。
    老年代:生存时间长,但总会死亡。eg:缓存对象,数据库连接对象,单例对象等等。
    永久代:几乎一直不灭。eg:String池中的对象,加载过的类信息。

    java堆:新生代,老年代 方法区(永久代):

    使用这样的方式,我们只浪费了10%的内存,这个是可以接受的,因为我们换来了内存的整齐排列与GC速度。第二点是,这个策略的前提是,每次存活的对象占用的内存不能超过这10%的大小,一旦超过,多出的对象将无法复制。
    为了解决上面的意外情况,也就是存活对象占用的内存太大时的情况,高手们将JAVA堆分成两部分来处理,上述三个区域则是第一部分,称为新生代或者年轻代。而余下的一部分,专门存放老不死对象的则称为年老代。

    JVM在进行GC时,并非每次都对上面三个区域一起回收,大部分回收的是新生代。因此GC按照回收的区域又分为两种:普通GC,全局GC
    普通GC:只针对新生代区域的GC
    全局GC:针对老年代的GC,偶尔伴随新生代的GC以及对永久带的GC

    由于年老代与永久代相对来说GC效果不好,而且二者的内存使用增长速度也慢,因此一般情况下,需要经过好几次普通GC,才会触发一次全局GC。

  • 相关阅读:
    商贸通帐套隐藏方法
    固定资产打开提示:上年度数据未结转!
    ZOJ 2432 Greatest Common Increasing Subsequence
    POJ 1080 Human Gene Functions
    POJ 1088 滑雪
    POJ 1141 Brackets Sequence
    POJ 1050 To the Max
    HDOJ 1029 Ignatius and the Princess IV
    POJ 2247 Humble Numbers
    HDOJ 1181 变形课
  • 原文地址:https://www.cnblogs.com/tian666/p/7908750.html
Copyright © 2011-2022 走看看