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

    一. 标记存活对象

    引用计数器法

    引用计数器法原理很简单:在对象中添加一个引用计数器,每有一个地方引用它时,计数器加一,引用失效时,计数器减一,计数器值为0时,这个对象就是不可再被使用的。这个算法理解起来简单,但实际应用时需要许多额外的处理与判断才能正常工作,比如循环引用的问题。

    可达性分析法

    java标记存活对象就是使用可达性分析法。基本思路是通过一系列称作“GCRoot”的根对象根据引用关系向下搜索,如果一个对象到一个GCRoot对象间没有任何路径相连,则这个对象就是不可达的,也就是会被垃圾处理器判定为可回收对象。

    可作为GCRoot的对象有以下几种

    1. 在虚拟机栈中引用的对象

    在程序中执行一个方法时,方法的局部变量等信息会保存至虚拟机栈中,在方法结束后出栈,所以在虚拟机栈中引用的对象都是可用的。

    2. 在方法区中类静态属性引用的对象

    也就是全局的静态变量,是线程公有的,所以必然要添加至GCRoot集合。

    3. 在方法区中常量引用的对象

    也就是常量池中的对象,初始化后不会再修改。

    4. 本地方法栈引用的对象

    PS. GCRoot集合类型不是固定不变的,在对区域进行回收时,需要考虑到其他区域对象对引用,一并加入到GCRoot集合中。

    引用的类型

    引用的传统定义:如果reference类型的数据的值代表另一块内存的起始位置,那么就称reference是这块内存的引用。

    JDK1.2后对引用对概念进行了扩充,分别为强引用、软引用、弱引用、虚引用,引用强度依次递减。

    • 强引用:就是指传统的引用,Object obj = new Object(); 只要强引用关系存在,被引用的对象就永远不会被清除。
    • 软引用:描述的是“还有用”但“非必须”的对象,系统要发生内存溢出异常前,会把这类型的对象列入回收范围进行二次回收,如果这些对象回收后内存仍然不足,那么将会抛出内存溢出异常。SoftReference类实现。可以实现内存敏感的高速缓存。
    • 弱引用:也是描述非必须的对象,但是引用强度弱于软引用,只能生存到下一次GC为止,无论内存是否足够。WeakReference类实现。
    • 虚引用:一个对象是否有虚引用存在,都不会对其生存时间构成影响,也无法通过虚引用获得一个对象实例。PhantomReference类实现。

    finalize()方法

    标记为不可达对对象,并非“非死不可”,要真正死亡,还需要经历两次标记过程:如果对象在经过可达性分析后发现没有路径可以达到GCRoot,则会被第一次标记。随后会经历一次筛选,判断这个对象是否有必要执行finalize()方法,如果对象没有覆盖finalize()方法或已经执行过,则被视为没有必要执行。如果被虚拟机视为有必要执行,那么虚拟机会触发这个对象的finalize()方法,但并不保证会执行成功。如果方法执行成功,那么在第二次标记时,对象将会被移除“即将回收”集合,否则将被回收。下面是一个使用finalize()方法自救的例子。

    class FinalizeEscapeGc {
    
        public static FinalizeEscapeGc SAVE_HOOK = null;
    
        public void isAlive() {
            System.out.println("yes alive!!!");
        }
    
        @Override
        protected void finalize() throws Throwable {
            super.finalize();
            System.out.println("finalize !!!");
            SAVE_HOOK = this;
        }
    
        public static void main(String[] args) throws InterruptedException {
    
            SAVE_HOOK = new FinalizeEscapeGc();
    
            SAVE_HOOK = null;
    
            System.gc();
    
            Thread.sleep(500L);
    
            if (Objects.nonNull(SAVE_HOOK)) {
                SAVE_HOOK.isAlive();
            } else {
                System.out.println("dead!");
            }
    
            SAVE_HOOK = null;
    
            System.gc();
    
            Thread.sleep(500L);
    
            if (Objects.nonNull(SAVE_HOOK)) {
                SAVE_HOOK.isAlive();
            } else {
                System.out.println("dead!");
            }
    
        }
    }
    

    输出

    finalize !!!

    yes alive!!!

    dead!

    回收方法区

    方法区的垃圾回收主要回收两部分内容:废弃的常量不再使用的类型

    废弃的常量举例:“java”曾进入常量池,但是现在没有任何一个字符串的值是“java”,也就是没有任何字符串对象引用“java”,如果这时发生GC,“java”将会被移出常量池。

    判断不再使用的类型需要满足三个条件:

    • 该类所有的子类都已被回收,堆中不存在该类及该类子类的实例。
    • 加载该类的类加载器已经被回收
    • 该类对应的class对象没有在任何地方被引用,无法通过反射访问该类的方法。
  • 相关阅读:
    C++基础知识(二)
    C++基础知识(一)
    RT-thread 设备驱动组件之IIC总线设备
    RT-thread 设备驱动组件之SPI设备
    RT thread 设备驱动组件之USART设备
    RT-thread 设备驱动组件之PIN设备
    RT-thread finsh组件工作流程
    C语言知识点
    RT-thread main函数分析
    堆和栈的区别
  • 原文地址:https://www.cnblogs.com/youtang/p/12685984.html
Copyright © 2011-2022 走看看