[Java]一篇文章浅入深出了解「GC原理」以及常见问题

前言

本章回顾的是:GC垃圾回收机制原理。
想回顾JVM的可以看这篇文章:[Java]一篇文章浅入深出了解「JVM原理」以及常见问题
常更新,欢迎收藏~

GC

通常我们判断一个对象判定为垃圾的标准,是有没有被其他对象引用

判定对象是否为垃圾的算法

  • 引用计数算法

    • 但是,Java中却没有使用这种算法,因为这种算法很难解决对象之间相互引用的情况。看一段代码。
    public class ReferenceCountingGC
    {
        private Object instance = null;
        private static final int _1MB = 1024 * 1024;
        
        /** 这个成员属性唯一的作用就是占用一点内存 */
        private byte[] bigSize = new byte[2 * _1MB];
        
        public static void main(String[] args)
        {
            ReferenceCountingGC objectA = new ReferenceCountingGC();
            ReferenceCountingGC objectB = new ReferenceCountingGC();
            objectA.instance = objectB;
            objectB.instance = objectA;
            objectA = null;
            objectB = null;
            
            System.gc();
        }
    }
    
  • 可达性分析算法

    这个算法的基本思想是通过一系列称为“GC Roots”的对象作为起始点,从这些节点向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链(即GC Roots到对象不可达)时,则证明此对象是不可用的。

    那么问题又来了,如何选取GCRoots对象呢?在Java语言中,可以作为GCRoots的对象包括下面几种:

    • 虚拟机栈(栈帧中的局部变量区,也叫做局部变量表)中引用的对象。

    • 方法区中的类静态属性引用的对象。

    • 方法区中常量引用的对象。

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

方法区的垃圾回收

方法区的垃圾回收主要回收两部分内容:1. 废弃常量。2. 无用的类。

如何判断无用的类呢?需要满足以下三个条件

  • 该类的所有实例都已经被回收,即Java堆中不存在该类的任何实例。

  • 加载该类的ClassLoader已经被回收。

  • 该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。

谈谈你了解的垃圾回收算法

  • 标记-清除算法(Mark and Sweep)

    • 分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,标记完成后统一回收所有被标记的对象。

    在这里插入图片描述

  • 复制算法(Copying)

    • 它将可用的内存分为两块,每次只用其中一块,当这一块内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已经使用过的内存空间一次性清理掉。

    在这里插入图片描述

  • 标记-整理算法(Compacting)

    • 让所有存活对象都向一端移动,然后直接清理掉边界以外的内存

    162F7FE5-AE04-4B84-8734-5964D3EBA192

  • 分代收集算法(Generational Collector)

    在这里插入图片描述

    • **新生代的内存被划分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden和其中一块Survivor。**每次回收时,将Eden和Survivor中还存活着的对象一次性复制到另外一块Survivor空间上,最后清理掉Eden和刚才用过的Survivor空间。HotSpot虚拟机默认Eden区和Survivor区的比例为8:1,意思是每次新生代中可用内存空间为整个新生代容量的90%。当然,我们没有办法保证每次回收都只有不多于10%的对象存活,当Survivor空间不够用时,需要依赖老年代进行分配担保(Handle Promotion)。
    • 大批对象死去、少量对象存活的(新生代),使用复制算法,复制成本低;对象存活率高、没有额外空间进行分配担保的(老年代),采用标记-清理算法或者标记-整理算法。
    • GC(Generational Collector)分类
      • Minor GC:新生代GC,每次 Minor GC 会清理年轻代的内存。触发机制:

        • 当年轻代满时就会触发Minor GC,这里的年轻代满指的是Eden代满,Survivor满不会引发GC。
      • Full GC:老年代 GC, 是清理整个堆空间—包括年轻代和永久代。触发机制:

        • 调用System.gc()时,系统建议执行Full GC,但是不必然执行
        • 老年代空间不足
        • 方法区空间不足
        • 通过Minor GC后进入老年代的平均大小大于老年代的可用内存
        • 由Eden区、survivor space1(From Space)区向survivor space2(To Space)区复制时,对象大小大于To Space可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小
      • Major GC:是清理老年代。

    • 常用的调优参数
      • -XX:SurvivoRatio:Eden和Survivor的比值,默认8:1
      • -XX:NewRatio:老年代和年轻代内存大小的比例
      • -XX:MaxTenuringThreshold:对象从年轻代晋升老生代经过GC次数的最大阙值
    • 参考

垃圾收集器

垃圾收集器就是上面讲的理论知识的具体实现了。不同虚拟机所提供的垃圾收集器可能会有很大差别,我们使用的是HotSpot,HotSpot这个虚拟机所包含的所有收集器如图:
在这里插入图片描述

  • 新生代
    • Serial收集器
    • ParNew收集器
    • Parallel Scavenge收集器
  • 老生代
    • Serial Old收集器
    • Parallel Old收集器
    • CMS收集器
  • G1收集器

Object的finalize()方法的作用是否与C++的析构函数作用相同

  • 与C++的析构函数不同,析构函数调用确定,而它不确定

  • 将未被引用对象放置于F-Queue队列

  • 方法执行随时可能会被终止

  • 给予对象最后一次重生机会

  • 通过代码理解

    package com.dai;
    
    /**
     * author daioo
     * create 2019-03-07 22:45
     */
    public class Finalization {
        public static Finalization finalization;
        @Override
        protected void finalize() {
            System.out.println("Finalized");
            finalization = this;
        }
        public static void main(String[] args) {
            Finalization f = new Finalization();
            System.out.println("First print:" + f);
            f = null;
            System.gc();
            try {// 休息一段时间,让上面的垃圾回收线程执行完成
                Thread.currentThread().sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Second print:" + f);
            System.out.println(f.finalization);
        }
    }
    

四种引用状态

  • 强引用(Strong Reference)

    • 代码中普遍存在的类似"Object obj = new Object()"这类的引用,只要强引用还存在,垃圾收集器永远不会回收掉被引用的对象。
    • 抛出OutOfMemoryError终止程序也不会回收具有强引用的对象;通过将对象设置为null来弱化引用,使其被回收
  • 软引用(Soft reference)

    • **描述有些还有用但并非必需的对象。在系统将要发生内存溢出异常之前,将会把这些对象列进回收范围进行二次回收。**如果这次回收还没有足够的内存,才会抛出内存溢出异常。

    • 可以用来实现高速缓存

      String str = new String("abc") // 强引用
      SoftReference<String> softRef = new SoftReference<String>(str); // 软引用
      
  • 弱引用(Weak Reference)

    • 描述非必需对象。被弱引用关联的对象只能生存到下一次垃圾回收之前,垃圾收集器工作之后,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。

      String str = new String("abc") // 强引用
      WeakReference<String> weakRef = new WeakReference<String>(str); // 弱引用
      
  • 虚引用(Phantom Reference)

    • 不会决定对象的生命周期,和其生存时间完全没关系。

    • 任何时候都可能被垃圾收集器回收

    • 跟踪对象被垃圾收集器回收的活动,起哨兵作用

    • 必须和引用队列ReferenceQueue联合使用

      String str = new String("abc") // 强引用
      ReferenceQueue queue = new ReferenceQueue();
      PhantomReference<String> softRef = new PhantomReference<String>(str, queue); // 虚引用
      
      • 引用类型关系被垃圾回收时间用途生存时间
        强引用有用必须对象从来不会对象的一般状态JVM停止运行时终止
        软引用有用非必须对象在内存不足时对象缓存内存不足(溢出)时终止
        弱引用非必须对象在垃圾回收时对象缓存gc运行后终止
        虚引用无用对象Unknown标记、哨兵垃圾收集时会收到系统通知
  • 引用队列(ReferenceQueue)
    在这里插入图片描述

    • 无实际存储结构,存储逻辑依赖于内部节点之间的关系来表达
    • 存储关联的且被GC的软引用,弱引用以及虚引用

参考

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值