我是靠谱客的博主 犹豫酸奶,最近开发中收集的这篇文章主要介绍Jvm-8—垃圾回收概述及相关算法,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

        8.1 概述 

        什么是垃圾?

        指运行过程中没有指针指向的对象。

        垃圾回收的主要范围是堆和方法区,最主要的是堆,有的Jvm并不涉及方法区的垃圾回收。分为标记(标识哪些是垃圾)和清除阶段。

        8.2 垃圾标记阶段的算法——引用计数算法

        对每个对象保存一个整型的引用计算器属性,用于记录对象被引用的次数情况。当有对象引用A时,A的引用计数器加1;当引用失效时,引用计数器减1.当引用计数器的值为0时 即表示对象A不可能再被使用,可进行回收。

        优点:实现简单,垃圾对象便于标识;回收效率高 没有延迟。

        缺点:需要单独的字段存储计数器,增加空间的开销;每次赋值都需要更新计数器,增加了时间的开销;致命的缺陷,无法处理循环引用的情况,导致Java没有采用引用技术算法。

当P指针断开连接时  此时无法回收  造成内存泄漏。

        证明JVM没有采用引用计算算法标识垃圾对象

public class gctest1 {
    private byte[] bigSize = new byte[5*1024*1024];

    Object reference = null;

    public static void main(String[] args) {
        gctest1 g1 = new gctest1();
        gctest1 g2 = new gctest1();

        g1.reference = g2;
        g2.reference = g1;
        g1 = null;
        g2 = null;

        //显示的调用gc
        //System.gc();
    }
}
/*
    Heap
 PSYoungGen      total 75776K, used 18043K [0x000000076b900000, 0x0000000770d80000, 0x00000007c0000000)
  eden space 65024K, 27% used [0x000000076b900000,0x000000076ca9ee00,0x000000076f880000)
  from space 10752K, 0% used [0x0000000770300000,0x0000000770300000,0x0000000770d80000)
  to   space 10752K, 0% used [0x000000076f880000,0x000000076f880000,0x0000000770300000)
 ParOldGen       total 173568K, used 0K [0x00000006c2a00000, 0x00000006cd380000, 0x000000076b900000)
  object space 173568K, 0% used [0x00000006c2a00000,0x00000006c2a00000,0x00000006cd380000)
 Metaspace       used 3347K, capacity 4556K, committed 4864K, reserved 1056768K
  class space    used 361K, capacity 392K, committed 512K, reserved 1048576K
*/

/*

显示的调用gc后
[GC (System.gc()) [PSYoungGen: 16742K->977K(75776K)] 16742K->985K(249344K), 0.0035305 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (System.gc()) [PSYoungGen: 977K->0K(75776K)] [ParOldGen: 8K->820K(173568K)] 985K->820K(249344K), [Metaspace: 3341K->3341K(1056768K)], 0.0048064 secs] [Times: user=0.11 sys=0.00, real=0.00 secs] 
Heap
 PSYoungGen      total 75776K, used 1951K [0x000000076b900000, 0x0000000770d80000, 0x00000007c0000000)
  eden space 65024K, 3% used [0x000000076b900000,0x000000076bae7c68,0x000000076f880000)
  from space 10752K, 0% used [0x000000076f880000,0x000000076f880000,0x0000000770300000)
  to   space 10752K, 0% used [0x0000000770300000,0x0000000770300000,0x0000000770d80000)
 ParOldGen       total 173568K, used 820K [0x00000006c2a00000, 0x00000006cd380000, 0x000000076b900000)
  object space 173568K, 0% used [0x00000006c2a00000,0x00000006c2acd228,0x00000006cd380000)
 Metaspace       used 3347K, capacity 4556K, committed 4864K, reserved 1056768K
  class space    used 361K, capacity 392K, committed 512K, reserved 1048576K
*/

        8.3 可达性分析(根搜索算法)GC Roots

        1.基本思路:

        以根对象(一组活跃的引用)为起始点,按照从上至下的方式搜索被根对象集合所连接的目标对象是否可达。

        使用可达性分析后,内存中存活的对象都会被根对象集合直接或间接的连接着,搜索过的路径称为引用链。如果内有被引用链连接 则对象被标记为垃圾对象。

        2.根对象:

  1. 虚拟机栈中的引用  如各个线程中使用到的参数、局部变量
  2. 静态类属性的引用对象
  3. 字符串常量池中的引用
  4. 虚拟机内部的引用: 基本数据类型对应的Class对象,常驻的异常对象(OutofMemoryError、NullPointerException)        
  5. 除了上述固定的GC Roots对象外,根据用户所选用的垃圾回收器以及当前回收的内存区域不同,还可以有其他对象“临时”加入。比如当回收新生代区域时,老年代中的对象也属于GC Roots,需要判断老年代中的引用是否指向了新生代中的对象。

        在使用可达性分析标记对象时,必须在一个保障一致性的快照中进行,不能在分析过程中对象的状态时刻在变化。这是Gc时STW的一个重要原因。

        8.4 finalize 方法

       1. 当一个对象被回收之前,垃圾回收器会调用这个对象的finalize方法。finalize方法允许在字类中重写,用于在对象被回收时进行资源释放。通常在这个方法中进行一些资源释放和清理的工作,比如关闭文件、套接字和数据库连接等。

        不要主动的调用finallize方法,应交给垃圾回收器调用。理由:

        在finalize时可能导致对象复活;

        finalize()的执行完全由GC线程决定,由优先级比较低的Finalizer线程执行,即使主动调用也可能不会发生

        糟糕的finalize方法会严重影响GC的性能。

        2.对象的三种状态

        可触及的:存在于引用链上

        可复活的:第一次检索时不在引用链上 但是finalize方法还未执行,可能在finalize中复活

        不可触及的:finalize被执行 ,并且没有复活。finalize只能被调用一次。   

        8.5 垃圾清除结算算法之标记-清除算法

        执行过程:当堆中的有效内存空间被耗尽的时候,就停止整个程序,然后进行两项工作。1标记2清除。

        标记:Collector从引用根节点开始遍历,标记所有被引用的对象。一般在对象的Header中记录为可达对象。标记的是非垃圾对象。

        清楚:Collector对堆内存从头到尾进行线性的遍历,如果发现某个对象在其Header中没有标记为可达对象,则将其回收。

 缺点:效率不高;GC时需要停止整个应用程序;此种方式清理出来得空闲内存不是连续得,会产生内存碎片,需要维护一个空闲列表。

清除的含义:清除并不是真的置空,而是把要清除的对象地址保存在空闲的地址列表中。下次有新对象需要加载时,判断垃圾的地址空间是否足够,如果够就存放。

        8.6 垃圾清除阶段算法之复制算法

        将内存空间分为两块,每次只使用其中的一块。在垃圾回收时将正在使用的内存中的存活对象复制到未被使用的内存块中,之后清除正在使用的内存块中的所有对象,交换两个内存的角色,最后完成垃圾回收。

 优点:没有标记和清除过程,高效简单;复制之后可以保持内存的连续性,不会出现碎片问题。

 缺点:需要两倍的内存空间;当垃圾较少 存活对象较多时,效率会变得很低

        应用场景:新生代中得对象大多数都是朝生戏死 存活对象较少,适合使用复制算法。

        8.7 垃圾清除阶段算法之标记-压缩算法

        在老年代,大多数都是存活对象,使用复制算法效率很低。使用标记-清除算法会产生碎片,结合两种算法,使用标记-压缩算法。

         标记-清除-移动压缩,避免内存碎片。

三种清除算法对比:

 针对不同内存区域得特点,使用不同得垃圾回收算法。年轻代区域较小,对象生命周期短,回收频繁,适合复制算法;老年代生命周期长,存活率高,回收不频繁,使用标记-压缩算法。

        

        

        

最后

以上就是犹豫酸奶为你收集整理的Jvm-8—垃圾回收概述及相关算法的全部内容,希望文章能够帮你解决Jvm-8—垃圾回收概述及相关算法所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(36)

评论列表共有 0 条评论

立即
投稿
返回
顶部