第三章(一)GC入门

本篇主要讲述 JAVA与GCJAVA与引用JAVA对象回收流程

垃圾回收(Garbage Collection,GC),是为了更加充分的使用系统、提高系统并发瓶颈的一种技术。

JAVA与GC

Java中的GC发生在方法区

Java内存运行区域中的程序计数器、虚拟机栈、本地方法栈这三个区域都是线程私有,随线程而生或灭。且其内存分配和回收具有确定性,所以不会考虑着三个区域。

而在GC时,我们需要知道哪些对象还活着,哪些已经死亡。一般有两类算法:引用计数器法可达性分析算法

  • 引用计数器法:通过给对象添加一个计数器来统计对象被未失效引用的次数,为0则回收;实现简单,效率高;
  • 可达性分析算法:所有的对象存在一棵多叉树中,多叉树的根为GC Roots,如果某个对象到GC Roots路径不可达,则判定回收。

在Java、C++等语言中,都是采用可达性分析算法,而JS等语言采用的是引用计数器法。Java采用可达性分析算法的原因是:引用计数器法对于Java对象之间的相互引用不能很好的处理。

JAVA中的引用

Java引用的扩充源于JDK1.2,分为以下四种:

  • 强引用(Strong Reference):类似“A a = new A()”之类的,只要强引用a还存在,new A()是永远不会被回收的。
  • 软引用(Soft Reference):有用但非必须的对象。在将要OOM,会将这些对象列入二次回收名单,在二次回收之后还是没有足够的内存才会抛出OOM。通过继承SoftReference来实现。
  • 弱引用(Weak Reference):非必须对象。无论内存是否足够,在一次GC时被强制回收。通过继承WeakReference来实现。
  • 虚引用(Phantom Reference):也叫幽灵引用或幻影引用,最弱的一种关系。虚引用对象不可获取实例。为对象设置虚引用的唯一目的是在该对象GC时会收到一个系统通知。通过继承PhantomReference来实现。

JAVA中的回收流程

回收流程.png

如图,JAVA回收流程图,关于图中几个知识点说一下:

  • finalize筛选:判断该对象是否有必要执行finalize()方法。判断条件:1)是否重写finalize()方法;2)其finalize()方法是否被JVM调用过。满足任何一个,判断回收,即没有必要执行finalize()。
  • finalize()方法只会被执行一次。第二次时会跳过。

Tips:尽量不要使用finalize(),其代价高,不确定性,且无法保证执行顺序。finalize()是为了让C/C++程序员转型而生的。finalize()能做的,try-finally都可以做,且做的更好,更可控。

方法区的回收

之所以在这里写方法区的回收,是因为该部分的回收重要也不重要。重要是因为该部分也会导致OOM,不重要是因为该区域被称为永久代,OOM异常一般情况下不常见。

该部分回收主要有两部分内容:废弃常量和无用的类。

常量池在JDK7+移入堆中。常量的回收比较简单,如果没有任何String对象引用到该常量,且此时发生GC,则回收该常量。
类的回收比较苛刻,需要同时满足一下条件,才算是“无用的类”:

  • 该类所有实例都被回收,即Java堆中不存在该类的任何实例;
  • 加载该类的ClassLoader已被回收;
  • 该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。

在满足上述3个条件后,虚拟机才 可以 对无用的类进行回收(此回收并不是必然的)。
相关HotSpot参数:
//TODO:需要查询参数意义以及使用
-Xnoclassgc是否对类进行回收
-verbose:class
-XX:+TraceClassLoading
-XX:+TraceClassUnLoading

方法区具备类卸载功能的场景:

  • 大量使用反射、动态代理、CGLib等技术;
  • 动态生成JSP;
  • OSGi这类频繁自定义ClassLoader的场景。

你可能感兴趣的:(第三章(一)GC入门)