• (六)垃圾回收算法


    一、标记-清除算法

    1) 标记-清除算法

    最基础的收集算法是“标记-清除”(Mark-Sweep)算法,如它的名字一样,算法分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收掉所有被标记的对象,它的标记过程其实在前一节讲述对象标记判定时已经基本介绍过了。之所以说它是最基础的收集算法,是因为后续的收集算法都是基于这种思路并对其缺点进行改进而得到的。它的主要缺点有两个:一个是效率问题,标记和清除过程的效率都不高;另外一个是空间问题,标记清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致,当程序在以后的运行过程中需要分配较大对象时无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。 标记-清除算法的执行过程如图

    二、复制算法

    为了解决效率问题,一种称为“复制”(Copying)的收集算法出现了,它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。这样使得每次都是对其中的一块进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况,只要移动堆顶指针,按顺序分配内存即可,实现简单,运行高效。只是这种算法的代价是将内存缩小为原来的一半,未免太高了一点。复制算法的执行过程如图

    三、标记整理算法

    3) 标记-整理算法

    复制收集算法在对象存活率较高时就要执行较多的复制操作,效率将会变低。更关键的是,如果不想浪费50%的空间,就需要有额外的空间进行分配担保,以应对被使用的内存中所有对象都100%存活的极端情况,所以在老年代一般不能直接选用这种算法。
    根据老年代的特点,有人提出了另外一种“标记-整理”(Mark-Compact)算法,标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存,“标记-整理”算法的示意图如图

    四、分代收集算法

    4) 分代收集算法

    当前商业虚拟机的垃圾收集都采用“分代收集”(Generational Collection)算法,这种算法并没有什么新的思想,只是根据对象的存活周期的不同将内存划分为几块。一般是把Java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。而老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用“标记-清理”或“标记-整理”算法来进行回收

    五、综合

    JVM内存模型中分两大块,一块是New Generation, 另一块是Old Generation. 在New Generation中,有一个叫Eden的空间,主要是用来存放新生的对象,还有两个Survivor Spaces(from,to), 它们用来存放每次垃圾回收后存活下来的对象。在Old Generation中,主要存放应用程序中生命周期长的内存对象,还有个Permanent Generation,主要用来放JVM自己的反射对象,比如类对象和方法对象等。
    1) 在New Generation块中,垃圾回收一般用复制算法,速度快。每次GC的时候,存活下来的对象首先由Eden拷贝到某个Survivor Space, 当Survivor Space空间满了后, 剩下的live对象就被直接拷贝到Old Generation中去。因此,每次GC后,Eden内存块会被清空

    2) 在Old Generation块中,垃圾回收一般用标记整理的算法,速度慢些,但减少内存要求.


    垃圾回收分多级,0级为全部(Full)的垃圾回收,会回收Old段中的垃圾;1级或以上为部分垃圾回收,只会回收New中的垃圾,内存溢出通常发生于Old段或Perm段垃圾回收后,仍然无内存空间容纳新的Java对象的情况。


    Out Of Memory 只发生在jvm对old和perm generation 回收后还不能获足够内存的情况.
    当生成一个新对象时,内存申请过程如下:
    A. JVM会试图为相关Java对象在Eden中初始化一块内存区域
    B. 当Eden空间足够时,内存申请结束。否则到下一步
    C. JVM试图释放在Eden中所有不活跃的对象(这属于1或更高级的垃圾回收), 释放后若Eden空间仍然不足以放入新对象,则试图将部分Eden中活跃对象放入Survivor区
    D. Survivor区被用来作为Eden及Old的中间交换区域,当Old区空间足够时,Survivor区的对象会被移到Old区,否则会被保留在Survivor区
    E. 当Old区空间不够时,JVM会在Old区进行完全的垃圾收集(0级)
    F. 完全垃圾收集后,若Survivor及Old区仍然无法存放从Eden复制过来的部分对象,导致JVM无法在Eden区为新对象创建内存区域,则出现”out of memory错误”


    造成full gc的原因
    new了很多对象,没有即时在主动释放掉->Eden内存不够用->不断把对象往old迁移->old满了->full gc

    总结:上面的内容就介绍了Java虚拟机如何管理对象的,我们也看到了上面主要就是收集算法和堆空间的从新划分,这样做的目的都是在于垃圾回收的高效执行,但是总归看来,如果对象交给系统来管理,在系统运行的过程效率肯定会有影响的,但是这有一点比较好,就是不需要手动管理,给程序猿带来方便。

  • 相关阅读:
    Springboot整合Thymeleaf,Thymeleaf页面引入静态资源
    SpringBoot 使用thymeleaf 跳转页面时,总是提示404找不到页面
    项目报错:jdbcUrl is required with driverClassName.
    Springboot配置拦截器无法跳转登录页面
    查看 SELinux状态及关闭SELinux
    Solaris平台,如何通过端口号快速查看PID(进程)
    getApplicationContext()、getBasecontext()、getApplication() 、getParent()
    android工程下不能运行java main程序的解决办法
    SDCardUtils
    BaseAdapter
  • 原文地址:https://www.cnblogs.com/shyroke/p/8167777.html
Copyright © 2020-2023  润新知