JVM-GC垃圾回收机制

对象的引用

  • 强引用
    普通的Java对象引用,这种对象引用,无论是否执行垃圾回收,只要对应的引用不被回收,就一直存在。
  • 软引用
    将强引用的结果传递给SoftReference。
    jdk提供SoftReference类实现软引用;系统在发生内存溢出异常之前,会把只被软引用的对象进行回收。
    用途:缓存。
  • 弱引用
    非必须的对象。
    jdk提供WeakReference类来实现弱引用,垃圾回收无论是否内存不足都会回收只被WeakReference关联的对象。
  • 虚引用(幽灵)
    为一个对象设置虚引用相关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知。
    jdk提供PhantomRefrence类来实现虚引用。

引用类型的强度:强引用>软引用>弱引用>虚引用

对象的回收

不可达的对象真正死亡需要两次标记
当不可达时标记第一次标记,当对象覆盖finalize()方法并且finalize()方法没有被虚拟机调用过,此对象将会放置在一个叫做F-Queue的队列之中,稍后由一个由虚拟机自动建立的、低优先级的Finalizer线程去触发这个方法,但并不承诺会等待它运行结束再执行垃圾回收。
finalize()方法是对象逃脱死亡命运的最后一次机会,稍后GC将对F-Queue中的对象进行第二次小规模的标记,如果对象要在finalize()中重新与引用链上的任何一个对象建立关联那么他被移除出“即将回收”的集合,否则就被回收了。

垃圾回收的方式

标记-清除算法

标记要清除的对象,同一清除。
不足:
标记和清除的效率都不高。
标记清除后会产生大量的空间碎片,空间碎片太多会导致程序需要分配较大对象时,无法找到连续的内存,而提前触发垃圾回收。

复制算法

它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。
优点:
无内存碎片,只要移动堆顶指针,按顺序分配内存即可,实现简单,运行高效。
缺点:
实际可用内存缩小为了原来的一半。

现在的商业虚拟机都采用这种收集算法来回收新生代。
1、将内存分为一块较大的Eden空间和两块较小的Survivor空间;
2、每次使用Eden和其中一块Survivor。
3、当回收时,将Eden和Survivor中还存活着的对象一次性地复制到另外一块Survivor空间上,并清理掉Eden和刚才用过的Survivor空间。
HotSpot虚拟机默认Eden和Survivor的大小比例是8:1:1;浪费10%。

GC的方式

Minor GC:
从年轻代空间(包括 Eden 和 Survivor 区域)回收内存被称为 Minor GC。每次 Minor GC 会清理年轻代的内存。

Major GC:
Major GC 是清理老年代或者永久代。

Full GC:
Full GC 是清理整个堆空间—包括年轻代和老年代或者永久代。

对象由新生代进入老年代的策略

三种方式进入老年代:
长期存活的对象将进入老年代:虚拟机给每个对象定义了一个对象年龄(Age)计数器。如果对象在Eden出生并经过第一次Minor GC后仍然存活,并且能被Survivor容纳的话,将被移动到Survivor空间中,并且对象年龄设为1。对象在Survivor区中每“熬过”一次Minor GC,年龄就增加1岁,当它的年龄增加到一定程度(默认为15岁),就将会被晋升到老年代中。对象晋升老年代的年龄阈值,可以通过参数-XX:MaxTenuringThreshold设置。

如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无须等到MaxTenuringThreshold中要求的年龄。

大对象直接进入老年代:虚拟机提供了一个-XX:PretenureSizeThreshold参数,令大于这个设置值的对象直接在老年代分配。这样做的目的是避免在Eden区及两个Survivor区之间发生大量的内存复制。
JVM-GC垃圾回收机制
JVM-GC垃圾回收机制