虚拟机-- 垃圾收集 对象 区域 时机

垃圾收集器与内存分配策略

一、 GC要做的事情

  1. 哪些内存需要回收 ?
  2. 什么时候回收 ?
  3. 如何回收 ?

二、 什么时候需要GC ?

  1. 内存溢出、内存泄漏等;
  2. 垃圾收集收成系统达到更高并发量的瓶颈时。

三、 正文

1 哪些内存需要回收 ?

  1. 基本不考虑

    • 部分: 程序计数器、虚拟机栈、本地方法栈
    • 原因: 在编译期基本确定了内存大小
  2. 主要考虑

    • 部分: Java堆和方法区
    • 原因: 运行期间动态分配内存。

2 什么时候回收 ?

  • 答: 对象已死的时候
    #### 问题来了,如何判断对象已死 ?
  1. 引用计数算法

    • 定义: 给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值加1;当引用失效时,计数器值就减1;
         任何时刻计数器为0的对象就是不可能再被使用的。
    • 好处: 实现简单,效率高
    • 缺陷: 很难解决对象之间互相循环引用的问题
  2. 可达性分析算法

    • 定义: 通过一系列的称为 GC Roots 的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用连,
         当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。
    • java中,GC Roots对象

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

    • 第一和第二种算法判定对象是否存活都与引用有关。
    • JDK 1.2 后,引用有了新的定义,以下顺序引用由强变弱

      • 强引用(Strong Reference)

      Object obj = new Object(); 程序中类似这样的就是强引用。

      • 软引用(Soft Reference)

      软引用关联的对象,在系统将要发生内存溢出异常之前,将会把这些对象列进 回收范围之中进行第二次回收;

      • 弱引用(Weak Reference)

      弱引用关联的对象,只能生存到下一次垃圾收集发生之前。

      • 虚引用(Phantom Reference)

      虚引用存在的唯一意义: 在对象被回收的时候,收到一个系统通知;

    • 补充:

      • 强引用是普遍存在。
      • 软引用和弱引用都是用来描述非必须的对象
      • 虚引用又称为幽灵引用或者幻影引用。
  4. 生存还是死亡 ?

    • 需要注意的是,通过可达性分析算法得到的不可达的对象,也不是非死不可的。
    • 一个对象的死亡,至少经历两次标记过程:

      • 1 可达性分析不可达的对象,被第一次标记并且进行一次筛选

        • 筛选的条件:此对象是否有必要执行finalize()方法,,虚拟机将以下两种情况都视为 没有必要执行。

          • 1 对象没有覆盖finalize()方法;
          • 2 finalize()方法已经被虚拟机调用过
      • 2 筛选被判定有必要执行finalize()方法,进入 F-Queue的队列,等待被线程执行该方法。

        • 线程: 虚拟机自动创建的、优先级低的Finzlizer线程。
        • 执行:所谓的执行是指虚拟机会触发这个方法,但不会一直等待它运行结束。

          • 原因:如果对象在Finzlize()方法中执行缓慢 或 发生死循环;可能导致F-queue队列中其他对象
               永远处于等待,甚至导致整个内存回收系统崩溃。
    • finalize() 方法是对象逃脱死亡命运的最后一次机会,稍后GC将对F-Queue中的对象进行第二次小规模的标记

      • 如果对象重新与引用链上的任何一个对象建立关联,(例如: 对象把自己的值 -赋值给某个类的变量或对象的成员变量),移出即将回收的集合;
      • 如果对象没有移出回收的集合,基本上确定了被回收。
    • 任何一个对象的finalize()方法只会被系统自动调用一次。
    • finalize(): 运行代价高昂,不确定性大,无法保证各个对象的调用顺序。
    • 使用 try-finally 完全可以替代 finalize()方法。
  5. 回收方法区

    • 方法区(HotSpot虚拟机中的永久代)
    • java 虚拟机规范中,不要求在方法区进行垃圾收集。 因为收集性价比比较低。

      • 堆新生代中,一次垃圾回收可以回收 70%- 95%的空间。
      • 永久代(方法区) 收集效率远低于此。
    • 永久代的垃圾收集分为两部分:

      • 废弃常量

        • 定义: 回收废弃常量与回收Java堆中的对象类似。
        • 判断依据: 常量池中的字面量的回收为例, 一个字符串”a“在常量池中,当前系统中没有String对象是"a",也没有其他
          地方引用这个字面量,这时候如果发生内存回收,没有必要的话,这个“a”常量会被系统清理出常量池。
        • 常量池中的其他类(接口)、方法、字段的符号引用也与此类似。

      • 无用的类

        • 判断依据:

          • 该类所有的实例已经被回收,换句话说就是Java堆中不存在该类的任何实例。
          • 加载该类的ClassLoader已经回收。
          • 该类对应的java.lang.class对象没有在任务地方被引用,无法在任何地方通过反射访问该类的方法。
        • 虚拟机可以对满足上述三个条件的对象进行回收,不是必然的。
    • 大量使用反射、动态代理、CGLib 等ByteCode框架、动态生成JSP以及OSGj这类频繁自定义ClassLoader的场景都需要虚拟机具备类卸载的功能,以保证永久代不会溢出。

欢迎访问:

github中更多笔记

上一篇:scala简要:类与对象


下一篇:青出于蓝-了不起的继承类 | 带你学《Java面向对象编程》之三十六