• GC算法


    一、什么是GC

      GC是垃圾收集的意思(Gabage Collection),在程序运行过程中会产生一部分不再使用的对象占用着内存空间,如果这些对象长期占用内存而不被清除,就会使得内存不足,而导致内存溢出。内存处理是编程人员容易出现问题的地方,忘记或者错误的内存回收会导致程序或系统的不稳定甚至崩溃。我们知道 C++ 程序员在编码过程中,都需要自己去管理内存,一旦内存没有回收就会导致程序或系统的不稳定甚至崩溃。JAVA为了解决这个问题就推出了这个自动清除无用对象的功能,或者叫机制,这就是GC,它可以自动监测对象是否超过作用域从而达到自动回收内存的目的,使得Java 程序员不用担心内存管理。

      System.gc() 和 Runtime.getRuntime().gc()在 java 中可以提醒GC做垃圾回收,但是仅仅是提醒,具体GC做不做垃圾回收不是人为可以控制的,因为 垃圾回收时不定时的。

      在 Java 中 GC 主要进行垃圾回收的位置是,堆和永久区。

     二、可触及性

      在垃圾回收的过程中,需要去识别什么是垃圾,这里存在一个可触及性的概念。

    • 可触及的  

         从根结点开始可以触及到这个对象,那么这个对象就具有可触及性。

    • 可复活的

        一些对象在当前状态不可被触及,但是过一段时间之后就会再次被触及,所以这种对象也是不可以被GC回收。一旦所有引用被释放就是可复活状态,因为在finalize()中可以复活。所以一般要避免使用finalize()方法,如果操作不当,有可能会导致对象复活而导致一些不可预见的错误,而且它的优先级比较低,什么时候会被调用也时一件不确定的事情,如果有相对应的操作可以使用try-catch-finally来代替。

    • 不可触及的  

        在finalize()之后,可能会进入不触及状态,不可触及的的对象不能被复活,可以直接被GC回收

      哪些元素可以作为根元素呢?

          ①. 本地方法栈中的JNI(Native方法)引用对象。

          ②. 栈中的本地变量表

          ③. 方法区中的常量

          ④. 类的静态变量

    三、GC算法

    • 引用计数法

         在堆中存储对象时,每个对象都会存在一个计数器,如果此对象被其他对象或者栈中局部变量等引用,则引用计数 +1 ,反之,如果被放弃引用则引用计数 -1.当一个对象的引用计数为 0 时,则说明没有被其他对象所引用,于是它就属于垃圾。但是,引用计数法存在一个问题,如果A对象引用B对象,B对象引用A对象,那么测试的计数器的值都会大于0,所以使得A对象和B对象不可以被回收。在java中没有使用,而是被python所使用。

        我们看下面例子:

        此时 A,C,E 对象的引用计数为 1 ,而B,D 则为 引用计数为2 ,F的引用计数为 0,所以,F对象则为即将被回收的对象,但是像 G 和 H 对象则互相引用,他们的引用计数均为1,所以此时 G,H两个对象将不会被回收,这也是引用计数法存在的一个最根本的问题。

    • 可达性分析算法

        可达性分析算法时用来判断对象是否存活的。这个算法主要时以从根元素(GC Roots)出发,向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的,则判定这个对象为可回收对象。

        我们看下面例子:

          此时,A,B,D是可达的,而C,D则时不可达的,无论C,D是否相互引用,都会被回收。

    • 标记清除法

         标记清除法时现代垃圾回收算法的思想基础。它主要分两个阶段,标记阶段,和清除阶段,其中标记阶段就是使用上面的可达性算法去判断,此对象是否被引用,将未被引用和被引用的对象进行区分,将被引用的对象进行标记。在清除阶段清除所有没有被标记的对象。

        我们看下面例子:

        其中灰色部分表示被标记的对象,黑色部分表示未被标记对象,白色部分表示被空闲空间,在标记阶段,通过根元素开始标记被引用的对象,标记结束之后回收未被标记的对象。

    • 标记压缩法

         标记压缩法是在标记清除法的基础上做了一定的优化,它适用与存活对象较多的内存空间,比如老年代。标记压缩法跟标记清除法一样首先需要标记被引用的对象,但是在清除阶段有一定的优化,它将被标记的对象放在内存的一端,然后将内存边界之外的内存全部清除。

        我们看下面例子:

        首先,将被引用的对象标记出来,然后将被引用的对象全部按照地址进行存放到内存的一端,然后清除边界外的所有内存。

    • 复制算法

         复制算法,也是对标记清除法的优化,而且它的效率更加高效,但是它不适合于老年代这种存活对象较多的内存,应为它将内存划分为大小完全相同的两块区域,而每次只使用一块,另一块则完全处于空闲状态,当进行回收对象时,它首先需要做的也是将被引用的对象进行标记,然后将这些被引用的对象复制到另一块内存中,将这块内存进行清理回收,然后将两块内存的职责进行调换,完成垃圾回收。

         我们看下面例子:

        首先,我们有两块完全一样大小的内存,一块不进行使用,完全空闲,另一块使用之后,在进行垃圾回收时进行标记被引用对象,然后将被引用对象复制到另一块内存中,将第一块内存进行回收,然后将两块内存职责交换。

    四、现代JMV中GC的算法使用

       在现代JVM中GC垃圾清理时不止使用一种算法,而是将不同的算法运用与不同的内存空间进行垃圾回收。

      现在,GC在进行垃圾回收时,老年代将使用标记清除法进行回收,而新生代则使用了标记压缩法和复制算法,首先第一块比较大的区域为 eden区,下面两块比较小的区域为from区和to区也叫做s0和s1区,最下面为老年代。eden区在进行垃圾回收时首先将大对象直接进入老年代,然后将其他的被标记的对象放入没有使用的这块空间中也就是to区,然后将eden区全部清空。from区进行回收时,首先将被标记,但是比较年轻(经历过垃圾回收次数少)的对象放入to区,然后将年龄比较大(经历过垃圾回收次数多)的对象放入老年代,然后将from区全部清空,最后将from区和to区进行交换,结束回收。

    五、分代思想

      根据对象存活的周期进行分类,短命的对象归为新生代,长命的对象归为老年代。

      新生代:刚刚被创建出来的对象就是新生代对象。比较适合复制算法,因为新生代对象相对较少。

      老年代:在新生代中的大对象无法进入from区的对象会直接进入老年代,多次垃圾回收都没有被回收的对象会进入老年代。比较适合标记清除法和标记压缩算法,因为在老年代的对象要么是生命周期比较长的对象,要么是系统级的对象,它们多数都处于存活的状态,如果使用复制算法,一方面比较浪费空间,一方面存活对象较多,复制速度较慢。

     六、Stop-The-World

      在java程序中,有极小的可能会出现全局停顿的情况,所以代码都会被挂起,只有native代码可以执行,但不能和JVM进行交互,此时多半是由GC引起的,因为在程序在运行的过程中会不断的产生垃圾对象,但是GC在进行垃圾操作时,为了可以将垃圾回收进行彻底,于是将java程序挂起,等到GC结束之后再继续运行java程序。此时就会导致服务长时间没有响应。一般GC速度是比较快的,但是如果内存比较大,垃圾比较多的时候就会使用比较长的时间进行垃圾回收。

      这时如果遇到HA系统的话就会出现一些不必要的问题。因为主机正在进行GC时服务器没有了响应,此时备机就会自动启动去代替主机,但是当主机GC结束之后,程序会继续运行,此时主机和备机就会一起使用,就有可能导致数据不一致。

    -------------------- END ---------------------

     

    最后附上作者的微信公众号地址和博客地址

    Herrt灬凌夜:https://www.cnblogs.com/wuyx/

  • 相关阅读:
    【JAVA笔记——道】JAVA对象销毁
    【JAVA笔记——道】并发编程CAS算法
    httpClientUtil的get请求
    python基础 day11 下 ORM介绍 sqlalchemy安装 sqlalchemy基本使用 多外键关联 多对多关系 表结构设计作业
    python基础 day11 上 数据库介绍 mysql 数据库安装使用 mysql管理 mysql 数据类型 常用mysql命令 事务 索引 python 操作mysql ORM sqlachemy学习
    Python基础 Day10 Gevent协程 SelectPollEpoll异步IO与事件驱动 Python连接Mysql数据库操作 RabbitMQ队列 RedisMemcached缓存 Paramiko SSH Twsited网络框架
    python基础 day9 进程、与线程区别 python GIL全局解释器锁 线程 进程
    python基础 day8 Socket语法及相关 SocketServer实现多并发
    python基础 day7 面向对象高级语法部分 异常处理 异常处理 Socket开发基础
    python基础 day6 面向对象的特性:封装、继承、多态 类、方法、
  • 原文地址:https://www.cnblogs.com/wuyx/p/9655698.html
Copyright © 2020-2023  润新知