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对象没有在任何地方被引用,无法通过反射访问该类的方法。
  • 相关阅读:
    UVA 254 Towers of Hanoi
    UVA 701 The Archeologists' Dilemma
    UVA 185 Roman Numerals
    UVA 10994 Simple Addition
    UVA 10570 Meeting with Aliens
    UVA 306 Cipher
    UVA 10160 Servicing Stations
    UVA 317 Hexagon
    UVA 10123 No Tipping
    UVA 696 How Many Knights
  • 原文地址:https://www.cnblogs.com/youtang/p/12685984.html
Copyright © 2011-2022 走看看