1、如何判断对象是否要被回收

  1、引用计数法

    在对象中添加一个引用计数器,当有引用指向对象时,引用计数加一,引用失效时,计数减一。引用计数为0时,代表将被回收。

    简单高效,但是难以解决循环引用问题。

  2、可达性分析算法

    “活着的”对象一定有从某个地方指向它的引用。

    从一系列的GC Root开始遍历,寻找到的对象都是“活着”的,没有找到的就认为它改被回收了。

    哪些对象可作为GC Root?

      1、虚拟机栈局部变量表中的引用指向的对象

      2、方法区中静态属性引用的对象

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

      4、本地方法栈中引用的对象

2、再谈引用

  单凭有无引用判断一个对象是否存活太过狭隘,如何实现空间足够时保留,空间不足时才回收对象?

  强引用:Object o = new Object()之类的引用,只要强引用存在,对象永远不会被回收。

  软引用(SoftReference):描述有用但非必须的对象,只有在将要发生内存溢出之前才会将其列入回收范围之内。

  弱引用:比软引用更弱,弱引用指向的对象只能存活于下次垃圾回收之前。垃圾回收发生时,无论内存是否足够,都会将弱引用指向的对象回收。

  虚引用:完全不能影响对象的存在(就跟没有一样),甚至不能取得对象实例。唯一作用是在对象被垃圾回收时接收一个系统通知。

3、不可达的对象就非死不可了吗?

  一个对象的回收过程需要经过两个标记(筛选)阶段。

  对象没有覆盖finalize方法或其finalize方法已经被JVM调用过都视为不用执行finalize方法

   深入了解jvm-2Edition-GC与内存分配策略_垃圾回收

  可以看到,即使是不可达对象,也能在finalize()方法中最后一次拯救自己。但仅此一次,因为任何一个对象的finalize方法仅会被JVM执行一次

4、回收方法区

  方法区储存了类的所有信息,极为重要,也很少改变。因此,对方法区的回收效率不高,条件也很苛刻。

  永久代(方法区)的垃圾回收主要针对废弃的常量和无用的类

  什么才是无用的类?

    1、该类的所有实例都已经被回收

    2、加载该类的ClassLoader已经被回收

    3、该类的Class对象没有被引用,无法通过反射访问该类的方法

    满足三个条件只代表可以回收,但不一定会被回收

5、垃圾回收算法

  1、标记-清除算法 Mark-Sweep

    效率不高,且容易产生内存碎片。

  2、复制算法 Coping

    将空间分为两部分,一部分用于创建对象,一部分用于回收时复制对象。

    回收时,将可达的对象复制到复制区域。剩下的对象被回收。

    划分空间时,将内存划分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden和Survivor中一块。Survivor中另一块用于复制。

    HotSpot默认的Eden和Survivor大小比例是8:1。

    当用于复制的Survivor空间不足以容纳下存活对象时,这些对象将通过分配担保机制进入老年代

  3、标记整理算法 Mark-Compact

    考虑老年代中对象存活率高,复制算法空间成本太高。因此引入标记-整理算法

    即将标记-清除算法的清除改为将对象都向一端移动的整理(内存紧缩)。

  4、分代收集算法

    根据对象的存活周期将内存划分为几块,针对每一块采取适当的算法。

    一般分为新生代老年代

    新生代对象创建消亡频繁,采用复制算法;

    老年代对象存活率高,采用标记-清理或标记-整理算法。

6、HotSpot采用的算法

  1、枚举根节点

    可达性分析必须在一致性的状态中执行,即分析过程中不能出现引用的改变。因此要停止所有Java线程的执行(Stop the World)。

    HotSpot使用了OopMap来记录类加载完成后或即时编译(Just In Time)过程中的引用位置。以便能不检查所有上下文和全局的引用位置便能枚举GC Roots。

  2、安全点

    HotSpot没有为每条指令都生成OopMap,而是只针对特定位置,这个位置就是安全点

    因此程序只能在到达安全点时才能停止并进行进行GC。

    如何让所有的线程都走到安全点?

      1、抢先式中断

        先中断所有线程,再让没有到达安全点的线程继续执行至安全点。

      2、主动式中断

        GC需要中断线程时,设置一个标记。每个线程都去轮询该标记,标记为真时,就自动到安全点中断并挂起。

  3、安全区域

    引用关系不会发生变化的代码片段

7、垃圾收集器

  1、Serial收集器  新生代

    单线程收集器,它工作时,必须停止其他所有线程。简单而高效。

  2、ParNew收集器  新生代

    Serial收集器的多线程版本。

  3、Parallel Scavenge收集器  新生代

    注重于提高吞吐量。

  4、Serial Old收集器  老年代

  5、Parallel Old收集器  老年代

  6、CMS收集器  老年代

  7、G1收集器  不分代

8、内存分配与回收策略

  1、对象优先在Eden分配

    大多数情况下,对象在新生代Eden区中分配,Eden区空间不足时,将发起一次Minor GC。

  2、大对象直接进入老年代

    因为大对象复制移动代价高,因此让其进入老年代,避免移动。

    可用XX:PretenureSizeThreshold参数设置大对象的阈值。

  3、长期存活的对象进入老年代

    每个对象有一个年龄计数器

    新生对象经过一次Minor GC仍然存活并能被Survivor空间装下,则会被移动到Survivor空间,并且年龄被设为1。

    每熬过一次Minor GC,年龄就增加1岁。

    年龄达到一定程度(默认为15),就升入老年代。

  4、动态年龄判定

    不一定要达到年龄阈值才能晋升。如果同龄对象的年龄总和大于Survivor空间一半,那么大于或等于该年龄的对象就可晋升。而无需受阈值限定。

  5、空间分配担保

    在发生Minor GC之前进行检查。

   深入了解jvm-2Edition-GC与内存分配策略_引用计数_02

  6、Minor GC和Full GC

    新生代GC(Minor GC):发生在新生代,非常频繁,速度也比较快。

    老年代GC(Full GC / Major GC):发生在老年代,一般Major GC经常会伴随至少一次的Minor GC。速度慢,代价高。

9、相应的JVM参数:

  9