Java虚拟机垃圾收集

2019-01-29  本文已影响0人  LeonardoEzio

从上一篇文章《JVM内存模型相关基本概念》中我们可以了解到,java内存运行时区域中的程序计数器、虚拟机栈、本地方法栈3个区域随线程而生,随线程而灭。在这几个区域内就不需要过多考虑回收的问题,因为方法结束或者线程结束时,内存自然就跟随着回收了。而堆和方法区则不同,一个接口的实现是多种多样的,多个实现类需要的内存可能不一样,一个方法中多个分支需要的内存也不一样,我们只能在程序运行的期间知道需要创建那些对象,分配多少内存,这部分的内存分配和回收都是动态的,垃圾收集器所关注的也是这一部分内容,后续所说的“内存”分配与回收也是指这一部分的内在。

垃圾回收

1. 如何判断对象已死

垃圾收集器在对堆进行回收之前,第一件事情就是要确定哪些对象还活着,哪些对象已经死去可以进行回收。判断对象是否存活主要有以下方法:

可达性分析算法判定对象是否存活.png

在Java语言中,可作为GC Roots的对象包括下面几种:

  1. 虚拟机栈(栈帧中的本地变量表)中引用的对象。
  2. 方法区中类静态属性引用的对象。
  3. 方法区中常量引用的对象。
  4. 本地方法栈中JNI(Native 方法)引用的对象。

2. Java中对引用的定义

在JDK1.2之前,Java中的引用的定义很传统:如果reference类型的数据中存储的数值代表的是另一块内存的起始地址,就称这块内存代表着一个引用。JDK1.2之后,将引用分为强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)、虚引用(Phantom Reference)四种,四种引用强度依次逐渐减弱。

详情参考:java中的四种引用类型


3. Finalize方法(一个有趣的对象自救案例)

要真正宣告一个对象死亡,至少要经历两次标记过程;如果对象在进行可达性分析后发现没有存在与GC Root相连接的引用链,那它将会被第一次标记并且进行一次筛选,筛选的条件是对象是否有必要执行finalize()方法。当对象没有覆盖,或者finalize()方法已经被虚拟机调用过,虚拟机将这两种情况都视为没有必要执行。

public class ObjectSaveTest {

    public static ObjectSaveTest SAVE_HOOK = null;

    public void isAlive (){
        System.out.println("yes , i am still alive");
    }

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("finalize is executed!!");
        SAVE_HOOK =  this;
    }

    public static void main(String[] args) throws Exception{
        SAVE_HOOK = new ObjectSaveTest();

        //对象第一次成功拯救自己   在finalize方法中,重新关联上引用
        SAVE_HOOK = null;
        System.gc();

        Thread.sleep(500);

        if(SAVE_HOOK != null){
            SAVE_HOOK.isAlive();
        }else {
            System.out.println("no , i am dead :( ");
        }


        //对象第一次未能成功拯救自己
        SAVE_HOOK = null;
        System.gc();

        Thread.sleep(500);

        if(SAVE_HOOK != null){
            SAVE_HOOK.isAlive();
        }else {
            System.out.println("no , i am dead :( ");
        }
    }
}
4. 回收方法区

方法区(HotSpot虚拟机中的永久代)的垃圾收集主要回收两部分的内容:废弃常量和无用的类。


5. 垃圾收集算法

6. 垃圾收集器
Serial收集器

由于CMS收集器整个工作过程中耗时最长的并发标记与并发清楚阶段都可以与用户线程同时工作(初始标记,重新标记依然要Stop the world),所以从总体上看:CMS收集器的内存回收过程是和用户线程一起并发执行的,因此CMS收集器也拥有了并发收集,低停顿的优点。同时它也存在着以下三个明显的缺点:

  1. 对CPU资源敏感;
  2. 无法处理浮动垃圾;
  3. 它使用的回收算法-“标记-清除”算法会导致收集结束时会有大量空间碎片产生。

G1收集器的运作大致分为初始标记、并发标记、最终标记、筛选回收这几个步骤。收集器在后台维护了一个优先列表,每次根据允许的收集时间,优先选择回收价值最大的Region(这也就是它的名字Garbage-First的由来)。这种使用Region划分内存空间以及有优先级的区域回收方式,保证了GF收集器在有限时间内可以尽可能高的收集效率(把内存化整为零)。

上一篇:JVM内存模型相关基本概念
下一篇:Java虚拟机内存分配与回收策略

上一篇 下一篇

猜你喜欢

热点阅读