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

    什么是垃圾回收

          程序运行会产生各种各种的数据,那么这些数据存在于内存当中,这些数据不可能是永久存在的,无效的资源对象需要进行垃圾回收,释放内存


    不同的编程语言都有GC垃圾回收


          java语言自带GC垃圾回收器,并且有JVM自动进行垃圾回收,程序员主要关注代码实现,不关注垃圾回收 System.gc();
          C/C++语言当中,程序员new一个对象,相当于申请了一块内存,如果需要释放资源得手动通过delete关键字进行内存的释放

    常用算法:

        程序在运行过程中,会产生大量的内存垃圾(一些没有引用指向的内存对象都属于内存垃圾,因为这些对象已经无法访问,程序用不了它们了,

      对程序而言它们已经死亡),为了确保程序运行时的性能,java虚拟机在程序运行的过程中不断地进行自动的垃圾回收(GC)。关于 JVM 的 GC 算法

      主要有下面几种:

    引用计数算法

        在对象头处维护一个counter,每增加一次对该对象的引用计数器自加,如果对该对象的引用失联,则计数器自减。当counter为0时,表明该对象已经被

      废弃,不处于存活状态。这种方式一方面无法区分软、虛、弱、强引用类别。另一方面,会造成死锁,假设两个对象相互引用始终无法释放counter,永远不

      能GC。

    可达性分析算法

        

        通过一系列为GC Roots的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连时,
      则证明该对象是不可用的。如果对象在进行可行性分析后发现没有与GC Roots相连的引用链,也不会理解死亡。它会暂时被标记上并且进行一次筛选,筛选
      的条件是是否与必要执行finalize()方法。如果被判定有必要执行finaliza()方法,就会进入F-Queue队列中,并有一个虚拟机自动建立的、低优先级的线程去执
      行它。稍后GC将对F-Queue中的对象进行第二次小规模标记。如果这时还是没有新的关联出现,那基本上就真的被回收了。
      可达性分析算法是通过枚举根节点来实现的,最重要的问题是GC停顿。为了确保一致性(即所有对象之间的关系是确定下来的)而导致GC进行时必须进行停顿。
      在HotSpot的中,使用OopMap的数据结构存储特定位置上的调试信息,存储栈上那个位置原来是什么东西,这个信息是在JIT编译时跟机器码一起产生的。因
      为只有编译器知道源代码跟产生的代码的对应关系。 这样,GC在扫描时就可以得知这些信息了。这样做的目的是使HotSpot能够快速准确的完成GC Roots枚
      举,以期望减少GC停顿所带来的影响。HotSpot没有在所有的指令生成OopMap,所以只是在“特定位置”记录这些信息,这些位置就是安全点。程序执行时并
      非在所有的位置上都能停顿下来GC,只有在到达安全点时才能暂停。安全点选取基本上是以“是否让程序长时间执行的特征”选定。此外,HotSpot虚拟机在安
      全点的基础上还增加了安全区域的概念,安全区域是安全点的扩展。在一段安全区域中能够实现安全点不能达成的效果。

    标记清除法(Mark-Sweep)

      算法思想:

          为每个对象存储一个标记位,记录对象的状态(活着或是死亡)。分为两个阶段,一个是标记阶段,这个阶段内,为每个对象更新标记位,检查对象

        是否死亡;第二个阶段是清除阶段,该阶段对死亡的对象进行清除,执行 GC 操作。

      优点

        最大的优点是,相比于引用计数法,标记—清除算法中每个活着的对象的引用只需要找到一个即可,找到一个就可以判断它为活的。
        此外,这个算法相比于引用计数法更全面,在指针操作上也没有太多的花销。更重要的是,这个算法并不移动对象的位置(后面俩算法涉及到移动位置的问题)。

      缺点

        很长的幽灵时间,判断对象已经死亡,消耗了很多时间,这样从对象死亡到对象被回收之间的时间过长。
        每个活着的对象都要在标记阶段遍历一遍;所有对象都要在清除阶段扫描一遍,因此算法复杂度较高。
        没有移动对象,导致可能出现很多碎片空间无法利用的情况。

    如下图所示:

        

     

    标记压缩算法  

      

      标记压缩算法可以解决标记清除算法的内存碎片问题。

      其算法可以看作三步:

      •   标记垃圾对象

      •   清除垃圾对象

      •   内存碎片整理

      其过程如下:

        首先标记除垃圾对象(黄色)

      

     

    清除垃圾对象

     

    内存碎片整理

     

    复制算法

      算法思想

        该算法将内存平均分成两部分,然后每次只使用其中的一部分,当这部分内存满的时候,将内存中所有存活的对象复制到另一个内存中,然后将之前的内存清空,只使用这部分内存,循环下去。

      注意:

        这个算法与标记-整理算法的区别在于,该算法不是在同一个区域复制,而是将所有存活的对象复制到另一个区域内。

      优点

        实现简单
        不产生内存碎片

      缺点

        每次运行,总有一半内存是空的,导致可使用的内存空间只有原来的一半

    分代算法

      

        分代算法基于复制算法和标记压缩算法。
        首先,标记清除算法、复制算法、标记压缩算法都有各自的缺点,如果单独用其中某一算法来做GC,会有很大的问题。
        例如,标记清除算法会产生大量的内存碎片,复制算法会损失一半的内存,标记压缩算法的碎片整理会造成较大的消耗。
        其次,复制算法和标记压缩算法都有各自适合的使用场景。
        复制算法适用于每次回收时,存活对象少的场景,这样就会减少复制量。
        标记压缩算法适用于回收时,存活对象多的场景,这样就会减少内存碎片的产生,碎片整理的代价就会小很多。
     
        分代算法将内存区域分为两部分:新生代和老年代。
        根据新生代和老年代中对象的不同特点,使用不同的GC算法。
        新生代对象的特点是:创建出来没多久就可以被回收(例如虚拟机栈中创建的对象,方法出栈就会销毁)。也就是说,每次回收时,大部分是垃圾对象,所以新生代适用于复制算法。
        老年代的特点是:经过多次GC,依然存活。也就是说,每次GC时,大部分是存活对象,所以老年代适用于标记压缩算法。

      新生代分为eden区、from区、to区,老年代是一整块内存空间,如下所示:

     

      分代算法执行过程

        首先简述一下新生代GC的整个过程(老年代GC会在下面介绍):新创建的对象总是在eden区中出生,当eden区满时,
      会触发Minor GC,此时会将eden区中的存活对象复制到from和to中一个没有被使用的空间中,假设是to区(正在被使用的from区
      中的存活对象也会被复制到to区中)。
     
      有几种情况,对象会晋升到老年代:
     
        超大对象会直接进入到老年代(受虚拟机参数-XX:PretenureSizeThreshold参数影响,默认值0,即不开启,单位为Byte,例
      如:3145728=3M,那么超过3M的对象,会直接晋升老年代)
      如果to区已满,多出来的对象也会直接晋升老年代
      复制15次(15岁)后,依然存活的对象,也会进入老年代
     
    此时eden区和from区都是垃圾对象,可以直接清除。
  • 相关阅读:
    Saltstack cmd.run 多项命令
    salt state.sls windows 传输文件
    mysql 时区更改;5.7 弱口令
    nginx 端口转发
    nohup 后台执行
    检测 nginx 关闭切换keepalived
    Centos 7 安装 dotnet 环境
    unison 双向镜像同步
    samba 配置参数详解
    数据结构与算法面试题80道(15)
  • 原文地址:https://www.cnblogs.com/wangdayexinyue/p/12420909.html
Copyright © 2011-2022 走看看