1.内存分配

垃圾回收 - 图1

参数:

新生代:-Xmn

堆空间:-Xms: 最大 ; -Xmx: 最小;

常见分配策略

  • 1)对象优先分配在Eden区;
  • 2)大对象直接进入老年代;
  • 3)长期存活的对象将进入老年代;

2. 回收机制

Full Gc

收集整个堆,包括新生代、老年、方法区;

触发条件

1)当需要在老年代分配内存空间,但没有足够的空间时;

2)System.gc();

3)触发Minor Gc 时,发现转移的空间 比 向老年代的大;

Minor Gc

收集新生代的垃圾

触发条件

Eden区空间耗尽时;

过程

当发生MinorGc时,Eden区和From s区存活的对象会被复制到 To s 区,然后 From s 和 From t 指针交换,保证 To s区 是空的;

Survivor区对象晋升位老年代对象的条件

当在 From s 和 To s去 被来回复制15次,会被扔到老年代;

3.判定对象已死?

引用计数法

给对象添加引用计数器,有地方引用+1,引用失效就-1

优点:实现简单,效率高,但目前主流虚拟机均未采用此方法;

缺点:无法解决循环引用的问题;

可达性技术法

通过一系列称为“GC root”的对象作为节点,从这些节点出发,节点走多的路径称为引用链,当一个对象到GC roots 没有任何引用链时,证明对象不可用;

可以作为GC roots 的对象包括下面几种:

  • 虚拟机栈中引用的对象
  • 本地方法栈中引用的对象
  • 方法区中类静态属性引用的对象
  • 方法区中常量引用的对象

引用

强引用

一般我们new 出来的对象都是强引用;

弱引用

在内存不足时会回收;

软引用

垃圾收集器扫描到就回回收;

虚引用

仅用来标记对象被垃圾回收的活动;

不可达对象并非“非死不可”

不可达对象会先被标记,放入一个队列中,当第二次标记前还没有与CRoot 建立联系,就会被回收掉;

如何判断一个常量是废弃常量?

无引用

如何判断一个类是无用的类?

方法区主要回收的就是无用的类。

无用类需要满足3个条件:

  • 1)该类的所有实例都被回收
  • 2)加载该类的ClassLoader已经被回收
  • 3)该类的java.lang.Class对象没有在任何地方被引用,无法通过反射访问该类;

4.垃圾回收算法

标记-清除

首先标记不需要回收的对象,标记完后清除要回收的对象;

缺点:

  • 效率问题
  • 空间问题(会产生大量不连续的碎片)

垃圾回收 - 图2

标记复制

将内存分为大小相等的两块,当1块用完后,将可用对象复制到另一块,一次性回收;

垃圾回收 - 图3

标记-整理

标记不可回收对象,将不可用回收对象统一移动到一端,然后直接清理掉边界以外的内存;

垃圾回收 - 图4

分代收集

将堆内存的对象分为新生代和老年代,根据不同端的特点选用不同的回收算法;

新生代:因每次新生代都会有大量对象死去,可以采用复制算法;仅需要少量的复制,就能完成垃圾收集;

老年代:存活几率高,采用标记-清楚,或者标记整理算法处理;

5.垃圾回收器

Serial收集器

单线程垃圾回收,用户线程和回收线程切换停顿时间太长(在进行垃圾回收的时候,用户线程必须⏸暂停);

新生代采用标记-复制算法,老年代代用标记-整理算法

ParNew收集器

Serial收集器的升级版本,

新生代采用 标记-复制算法;老年代采用 标记-整理算法;

Parallel Scavenge 收集器

更关注吞吐量,提供很多参数,供选择帮用户找到最大吞吐量; 也可以采用自适应策略,将内存优化交给虚拟机处理;

java8 默认的垃圾回收器: Parallel Scavenge + Parallel Old;

Parallel Old 收集器

Parallel Scavenge 收集器的老年代版本。使用多线程和“标记-整理”算法。在注重吞吐量以及 CPU 资源的场合,都可以优先考虑 Parallel Scavenge 收集器和 Parallel Old 收集器;

CMS收集器

  • 收集器是一种以获取最短回收停顿时间为目标的收集器;
  • HotSpot 虚拟机第一款真正意义上的并发收集器,它第一次实现了让垃圾收集线程与用户线程(基本上)同时工作;
  • 优点:并发收集、低停顿;
  • 缺点:对CPU资源敏感; 使用标记-清除 会产生大量的内存碎片;

G1收集器

相对于CMS改进是,停顿时间可以预测;

  • 采用标记-清楚算法,会产生内存碎片

参见