1.1 对象优先在Eden区分配

目前主流的垃圾收集器都会采用分代回收算法,因此需要将堆内存分为新生代和老年代,这样就可以根据各个年代的特点选择合适的垃圾收集算法

  • 大多数情况下,对象在新生代中Eden区分配。当Eden区没有足够的空间进行分配时,虚拟机将发起一次Minor GC。
  • 新生代GC(Minor GC):发生新生代的来及收集动作,Minor GC非常频繁,回收速度一般比较快。
  • 老年代GC(Major GC/Full GC):发生在老年代的GC,出现Major GC经常会伴随最少一次的Minor GC(非绝对)Major GC的速度比Minor GC慢10倍以上。
    image
  • run configuration: -XX:+PrintGCDetails
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // Test
    public static void main(String[] args) {
    byte[] allocation1, allocation2;
    allocation1 = new byte[30900*1024];
    //allocation2 = new byte[900*1024];
    }
    /**
    * 给allocation2分配内存的时候Eden区内存几乎已经被分配完了,
    虚拟机将发起一次Minor GC,期间虚拟机又发现allocation1无法存入survivor空间,
    所以只好通过分配担保机制把新生代的对象转移到老年代中区,老年代的空间足够存放allocation1,
    所以不会出现full GC,执行Minor GC后,后面分配的对象如果能够存放Eden区的花,还是会在Eden区分配内存。
    **/
2.大对象分配内存时,为了避免由于分配担保机制而引起的复制效率问题,直接进入老年代

分配担保机制

3. 长期存活的对象将进入老年代
  • 虚拟机采用了分代收集的思想来管理内存,那么内存回收时就必须识别哪些对象应放在新生代,哪些对象应放在老年代中。所以虚拟机给每个对象一个对象年龄计数器。
  • 如果对象在Eden出生并经过第一次Minor GC后仍然能够存活,并且能够被survivor容纳的话,将被移动到survivor空间中,并将对象年龄设为1. 对象在survivor中每经历过一次Minor GC,年龄就增加1岁,当它的年龄增加一定程度(默认15),就会晋升到老年代中。
4.动态对象年龄判定

为了适应不同程序的内存情况,虚拟机不是永远要求对象年龄必须达到某个值才能进入老年代,如果survivor空间中相同年龄所以对象大小的总和大于survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无需达到年龄要求。

5.如何判断对象已经死亡

image

5.1引用计数法
  • 给对象中添加一个引用计数器,每当有一次地方引用它,计数器加1,当引用失效,计数器就减1,任何时候计数器为0的对象就是不可能再被使用的。
  • 该方法实现简单,效率高,但是它很难解决对象之间相互循环引用的问题。
    5.2可达性分析算法
    基本思想:通过一系列的称为“GC Roots”的对象作为起点,从这些节点开始向下搜索,节点所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链路相连的话,则证明该对象不可用了。
    image
    5.3再谈引用
    JDK1.2之后,Java对引用进行了扩充,将引用分为强引用,软引用,弱引用,虚引用
  • 强引用:必不可少,垃圾回收器永远不会回收它,内存不足时,JVM将抛出OutOfMemoryError错误。
  • 软引用:可有可无,内存空间足够,不会回收,内存空间不足,将会回收。只要垃圾回收器没有回收它,该对象就可以被程序使用。如果软引用所引用的对象被垃圾回收,JVM就会把这个软引用加入到与之关联的引用队列中。
  • 弱引用:可有可无,无论内存空间,都将会被回收。
  • 虚引用:形同虚设,任何时候都可能被垃圾回收。
    虚引用主要用来跟踪对象被垃圾回收的活动
  • 虚引用与软引用和弱引用的一个区别在于: 虚引用必须和引用队列(ReferenceQueue)联合使用。当垃 圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是 否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。程序如果发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动
    5.4不可达对象并非“非死不可”
    即使在可达性分析法中不可达的对象,并非“非死不可”,这个时候他们暂时处于“缓刑阶段”,要真正宣告一个对象死亡,至少要经历两次标记过程;可达性分析法中不可达的对象被第一次标记并且进行一次筛选,筛选的条件是此独享是否有必要执行finalize方法,当对象没有覆盖finalize方法,或者finalize方法已经被虚拟机调用过了,虚拟机将这个两种情况视为没有必要执行。
  • 被判定为需要执行的对象将会被放在一个队列中进行第二次标记,除非这个对象与引用链上的任何一个对象建立关联,否则就会被真的回收。
    6.废弃常量
  • 运行时常量池主要回收的就是废弃的常量。
  • 7.废弃的类
  • 方法区主要回收的是无用的类
  • 判断一个类是否无用,需要满足3个条件
    • 该类所有的实例都已经被回收
    • 加载该类的ClassLoader已经被回收
    • 该类对应的Class 对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。

8.垃圾收集算法

image

8.1 标记-清除算法
  • 首先标记处所有需要回收的对象,在标记完成后统一回收所有被标记的对象。
  • 1.效率问题
  • 2.空间问题-标记清除后会产生大量不连续的碎片
    image
    8.2 复制算法
  • 为了解决效率问题,
  • 他可以将内存分为大小相同的两块,每次使用其中的一块。当这一块的内存使用完后,就将还存活的对象复制到另一块去,然后把使用的空间一次清理掉,这样每次的内存回收都是对内存区间的一半进行回收。
    image
    8.3 标记-整理算法
  • 和标记-清除算法一样,不同之处是后续将所有存活的对象向一端移动,然后直接清理掉端边界以外的内存。
    image
8.4分代收集算法
  • 根据对象存活周期的不同将内存分为几块,一般分为新生代和老年代。这样就可以根据各年代的特点选择合适的垃圾收集算法
  • 新生代中:每次收集都会有大量对象的死去,所以采用复制算法,只需要将少量对象复制,就可以完成每次的垃圾收集。
  • 老年代中:存活率较高,没有额外的空间进行分配担保,就必须选择“标记-清除”或标记-整理算法进行垃圾回收。
9.垃圾收集器

image
如果说收集算法是内存回收的方法论,那么垃圾收集器就是内存回收的具体实现。
我们需要根据具体应用场景选择合适的垃圾收集器。

9.1 Serial收集器
  • 串行收集器:,他是一个单线程的收集器。它只会使用一条垃圾收集线程去完成垃圾收集工作,更重要的是它在进行垃圾收集工作的时候必须暂停其他所有的工作线程(Stop The World),直到收集结束。
  • 新生代采用复制算法,老年代采用标记-整理算法。
  • serial收集简单高效,没有线程交互的开销。
  • serial收集器对于运行在Client模式下的虚拟机来说是个不错的选择。
9.2 ParNew收集器
  • ParNew收集器就是Serial收集的多线程版本。其余行为(控制参数,收集算法,回收策略)和Serial收集器完全一样。
  • 新生代采用复制算法,老年代采用标记-整理算法。
  • 它适合运行在server模式下的虚拟机。
    并行和并发的概念
  • 并行(Parallel): 指多条垃圾收集线程并行工作,但此时用户线程任然处于等待状态。
  • 并发(Concurrent):指用户线程与垃圾收集线程同时执行,用户程序在继续运行,而垃圾收集器运行在另一个CPU上。
9.3Parallel Scavenge收集器
  • Parallel Scavenge收集器关注点是吞吐量(高效的利用CPU)
  • 吞吐量:CPU中运行用户代码的时间与CPU总消耗时间的比值。
  • 新生代采用复制算法,老年代采用标记-整理算法。
9.4CMS收集器
  • CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器,他适合注重用户体验的应用上使用。
  • CMS收集器是HotSpot虚拟机真正意义上的并发收集器。
  • CMS是标记-清除-整理算法的一种实现,其步骤分为以下四步:
    • 初始标记:暂停所有的其他线程,记录下直接与root相连的对象,速度很快。
    • 并发标记:同时开启GC和用户线程,用一个闭包结构去记录可达对象,但是闭包不能保证包好所有的可达对象。因为用户线程还在不断的更新引用域。
    • 重新标记:为了修正并发标记期间因为用户线程继续运行而导致标记产生变动的那部分对象的标记记录。
    • 并发清除:开启用户线程,同时GC线程开始对未标记的区域做清扫。
  • CMS收集器主要优点是: 并发收集, 低停顿
  • CMS收集器主要缺点是:
    • 对CPU资源敏感
    • 无法处理浮动垃圾
    • 采用标记-清除算法会导致收集结束后产生大量空间碎片。
9.5 G1收集器

G1(Garbage First)是一款面向服务器的垃圾收集器,主要针对配备多颗处理器及大容量内存的机器,以极高概率满足GC停顿时间要求的同时,还具备高吞吐量性能特征。

  • 其具备以下特点:
    • 并行与并发:充分利用CPU,多核等硬件优势,缩短stop the world的停顿时间。
    • 分代收集:
    • 空间整合:采用标记-整理算法。
    • 可预测的停顿:
  • G1收集器运作主要分为以下步骤:
    • 初始标记
    • 并发标记
    • 最终标记
    • 筛选回收