ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

垃圾回收算法

2022-06-19 11:02:35  阅读:122  来源: 互联网

标签:对象 内存 回收 算法 GC 垃圾 引用


 如何判断对象已死?

JVM 中判断对象是否已经死亡的算法主要有 2 种:引用计数法、可达性分析法。

引用计数法

  • 如果一个对象被其他变量所引用,则让该对象的引用计数+1,如果该对象被引用2次则其引用计数为2,依次类推。
  • 某个变量不再引用该对象,则让该对象的引用计数-1,当该对象的引用计数变为0时,则表示该对象没用被其他变量所引用,这时候该对象就可以被作为垃圾进行回收。
  • 引用计数法弊端:循环引用时,两个对象的引用计数都为1,导致两个对象都无法被释放回收。最终就会造成内存泄漏!
  •  

     可达性分析算法

  • 可达性分析算法就是JVM中判断对象是否是垃圾的算法:该算法首先要确定GC Root(根对象,就是肯定不会被当成垃圾回收的对象)。

    在垃圾回收之前,JVM会先对堆中的所有对象进行扫描,判断每一个对象是否能被GC Root直接或者间接的引用,

  • 如果能被根对象直接或间接引用则表示该对象不能被垃圾回收,反之则表示该对象可以被回收:

  • JVM中的垃圾回收器通过可达性分析来探索所有存活的对象。
    扫描堆中的对象,看能否沿着GC Root为起点的引用链找到该对象,如果找不到,则表示可以回收,否则就可以回收。
    **在Java技术体系里面,固定可作为GC Roots的对象包括以下几种 **:
    虚拟机栈(栈帧中的本地变量表)中引用的对象,譬如各个线程被调用的方法堆栈中使用到的参数、局部变量、临时变量等。
    在方法区中类静态属性引用的对象,譬如Java类的引用类型静态变量。
    在方法区中常量引用的对象,譬如字符串常量池(String Table)里的引用。
    在本地方法栈中JNI(即通常所说的Native方法)引用的对象。
    所有被同步锁(synchronized关键字)持有的对象。
    Java虚拟机内部的引用,如基本数据类型对应的Class对象,一些常驻的异常对象(比如 NullPointExcepiton、OutOfMemoryError)等,还有系统类加载器。

    Java中的五种引用

强引用

new 一个对象M,将对象M通过=(赋值运算符),赋值给某个变量m,则变量m就强引用了对象M。

强引用的特点:只要沿着GC Root的引用链能够找到该对象,就不会被垃圾回收;只有当GC Root都不引用该对象时,才会回收强引用对象。

软引用

软引用的特点:当GC Root指向软引用对象时,若内存不足,则会回收软引用所引用的对象。

如果在垃圾回收时发现内存不足,在回收软引用所指向的对象时,软引用本身不会被清理。

如果想要清理软引用,需要使用引用队列:

弱引用

只有当弱引用引用该对象,在垃圾回收时,无论内存是否充足,都会回收弱引用所引用的对象。

虚引用

当引用的对象ByteBuffer被垃圾回收以后,虚引用对象Cleaner就会被放入引用队列中:然后调用Cleaner的clean方法(Unsafe.freeMemory())来释放直接内存:

  • 虚引用的一个体现是释放直接内存所分配的内存,当引用的对象ByteBuffer被垃圾回收以后,虚引用对象Cleaner就会被放入引用队列中,然后调用Cleaner的clean方法来释放直接内存。

终结器引用

所有的类都继承自Object类,Object类有一个finalize()方法。当某个对象不再被其他的对象所引用时,会先将终结器引用对象放入引用队列中,然后根据终结器引用对象找到它所引用的对象,然后调用该对象的finalize()方法。调用以后,该对象就可以被垃圾回收了。

引用队列

  • 软引用和弱引用可以配合引用队列(也可以不配合):
    • 在弱引用和虚引用所引用的对象被回收以后,会将这些引用放入引用队列中,方便一起回收这些软/弱引用对象。
  • 虚引用和终结器引用必须配合引用队列:
    • 虚引用和终结器引用在使用时会关联一个引用队列。
    • 回收方法区

    • 方法区的垃圾收集主要回收两部分内容:废弃的常量和不再使用的类型。举个常量池中字面量回收的例子,假如一个字符串“java”曾经进入常量池中,但是当前系统又没有任何一个字符串对象的值是“java”,换句话说,已经没有任何字符串对象引用常量池中的“java”常量,且虚拟机中也没有其他地方引用这个字面量。如果在这时发生内存回收,而且垃圾收集器判断确有必要的话,这个“java”常量就将会被系统清理出常量池。常量池中其他类(接口)、方法、字段的符号引用也与此类似。

    • 判定一个常量是否“废弃”需要同时满足下面三个条件:
      • 该类所有的实例都已经被回收,也就是Java堆中不存在该类及其任何派生子类的实例。
      • 加载该类的类加载器已经被回收,这个条件除非是经过精心设计的可替换类加载器的场景,如 OSGi、JSP 的重加载等,否则通常是很难达成的。
      • 该类对应的 java.lang.Class 对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
      • 垃圾回收算法

      • 在Java堆划分出不同的区域之后,垃圾收集器才可以每次只回收其中某一个或者某些部分的区域——因而才有了“Minor GC”“Major GC”“Full GC”这样的回收类型的划分;也才能够针对不同的区域安排与里面存储对象存亡特征相匹配的垃圾收集算法——因而发展出了“标记-复制算法”“标记-清除算法”“标记-整理算法”等针对性的垃圾收集算法。
        四种GC概念的介绍:

      • 新生代收集(Minor GC/Young GC):指目标只是新生代的垃圾收集。

      •  老年代收集(Major GC/Old GC):指目标只是老年代的垃圾收集。目前只有CMS收集器会有单独收集老年代的行为。

      •  混合收集(Mixed GC):指目标是收集整个新生代以及部分老年代的垃圾收集。目前只有G1收集器会有这种行为。

      •  整堆收集(Full GC):收集整个Java堆和方法区的垃圾收集。

         

      •  标记-清除

      • 定义:标记清除算法顾名思义,是指在虚拟机执行垃圾回收的过程中,先采用标记算法确定可回收对象,然后垃圾收集器根据标识,清除相应的内容,给堆内存腾出相应的空间。
      • 这里的腾出内存空间并不是将内存空间的字节清 0,而是记录下这段内存的起始结束地址,下次分配内存的时候,会直接覆盖这段内存。
      • 缺点:(容易产生大量的内存碎片,可能无法满足大对象的内存分配,一旦导致无法分配对象,那就会导致jvm启动gc)。

        第一个是执行效率不稳定,如果Java堆中包含大量对象,而且其中大部分是需要被回收的,这时必须进行大量标记和清除的动作,导致标记和清除两个过

        程的执行效率都随对象数量增长而降低;

        第二个是内存空间的碎片化问题,标记、清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致当以后在程序运行过程中需要分配较大对象时无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。

         标记-整理

      • 标记-整理:会将不被GC Root引用的对象回收,清除其占用的内存空间。然后整理剩余的对象,可以有效避免因内存碎片而导致的问题,但是牵扯到对象的整理移动,需要消耗一定的时间,所以效率较低。
      • 标记-复制

      • 复制算法:将内存分为等大小的两个区域,FROM和TO(TO中为空)。先将被GC Root引用的对象从FROM放入TO中,再回收不被GC Root引用的对象。然后交换FROM和TO。这样也可以避免内存碎片的问题,但是会占用双倍的内存空间。
      • 分代回收

      • 把分代收集理论具体放到现在的商用Java虚拟机里,设计者一般至少会把Java堆划分为新生代(Young Generation)和老年代(Old Generation)两个区域,顾名思义,在新生代中,每次垃圾收集时都发现有大批对象死去,而每次回收后存活的少量对象,将会逐步晋升到老年代中存放。

      • 新创建的对象都被放在了新生代的伊甸园中
      • 当伊甸园中的内存不足时,就会进行一次垃圾回收,这时的回收叫做 Minor GC:

        Minor GC 会将伊甸园和幸存区FROM仍需要存活的对象先复制到 幸存区 TO中, 并让其寿命加1,再交换FROM和TO。

      • 伊甸园中不需要存活的对象清除
      • 继续向伊甸园新增对象,如果满了,则进行第二次Minor GC:
      • 再次创建对象,若新生代的伊甸园又满了,则会再次触发 Minor GC(会触发 stop the world, 暂停其他用户线程,只让垃圾回收线程工作),这时不仅会回收伊甸园中的垃圾,还会回收幸存区中的垃圾,再将活跃对象复制到幸存区TO中。回收以后会交换两个幸存区,并让幸存区中的对象寿命加1!

      • 如果幸存区中的对象的寿命超过某个阈值(最大为15,4bit),就会被放入老年代中:
      • 如果新生代老年代中的内存都满了,就会先触发Minor Gc,再触发Full GC,扫描新生代和老年代中所有不再使用的对象并回收:
      • 分代回收小结:

        新创建的对象首先会被分配在伊甸园区域。
        新生代空间不足时,触发Minor GC,伊甸园和 FROM幸存区需要存活的对象会被COPY到TO幸存区中,存活的对象寿命+1,并且交换FROM和TO。
        Minor GC会引发 Stop The World:暂停其他用户的线程,等待垃圾回收结束后,用户线程才可以恢复执行。
        当对象寿命超过阈值15时,会晋升至老年代。
        如果新生代、老年代中的内存都满了,就会先触发Minor GC,再触发Full GC,扫描新生代和老年代中所有不再使用的对象并回收。

 

标签:对象,内存,回收,算法,GC,垃圾,引用
来源: https://www.cnblogs.com/2246781190zyc/p/16390087.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有