GC

首先 垃圾回收算法并非Java独创,而是在Java很早之前就已经出现了的。

需要考虑三个问题

  1. 那些内存需要回收

  2. 什么时候回收

  3. 如何回收

那些内存需要回收

在开发中存在两种判断方式

计数算法

在对象中设置一个计数器,作为引用次数。

引用一次就在这个计数器上 + 1

结束引用就在计数器中 - 1

确实是一个比较简单高效的办法,但是无法去判断孤立的对象集合

或自引用的对象集合

对象A中包含对象B

对象B中包含对象A

导致A的计数器恒定 1 B的计数器也是恒定1

可达性算法

为了解决上述自引用的问题,Java采用的也是这种可达性算法

虚拟机会定义一个树形结构 GC Roots 把对象进行连接 查看对象的可达性

如果不可达 就认为对象是无法访问的 是可回收的对象

就像上图中 对象 B,C,D 虽然都互相引用,但是因为没有与GCRoots的连接 所以也算作是没有用的对象

会被进行回收

GC Roots

  1. 在虚拟机栈(栈帧中的本地变量表)中引用的对象,譬如各个线程被调用的方法堆栈中使用到的参数、局部变量、临时变量等。

  2. 在方法区中类静态属性引用的对象,譬如Java类的引用类型静态变量。

  3. 在方法区中常量引用的对象,譬如字符串常量池(String Table)里的引用。

  4. 本地方法栈中 JNI(Native方法)引用的对象

  5. Java虚拟机内部的引用,如基本数据类型对应的Class对象,一些常驻的异常对象(比如NullPointExcepiton、OutOfMemoryError)等,还有系统类加载器。

  6. 所有被同步锁(synchronized关键字)持有的对象。

  7. 反映Java虚拟机内部情况的JMXBean、JVMTI中注册的回调、本地代码缓存等。

  8. 根据用户所选用的垃圾收集器以及当前回收的内存区域不 同,“临时性”地加入的其他对象。

引用

上述多次提到引用,在现代的 Java 版本中 对 引用概念进行扩充

分为以下几种类型

  1. 强引用 就是引用-赋值,类似 Object test = new Object(); 这就是一个强引用

    1.  只要引用关系还在,GC就永远不会回收这些对象

  2. 软引用 用来描述一些,有用,但是用途不大的对象类型

    1.   在内存告急的情况下,触发了两次GC才会把软引用的对象进行回收

  3. 弱引用 只能存活到下次GC回收前

  4. 虚引用 不会对回收产生任何影响,他的作用是在被回收时输出一个日志

方法区回收

方法区在不同JVM实现也不同,部分JVM也未实现方法区的数据回收。部分JVM的方法区在元空间。

要回收方法区的数据要有三个条件:

  1. 该类所有实例都被回收,Java堆中不存在该类的任何派生子类的实例。

  2. 加载该类的类加载器已经被回收,这个条件除非是经过精心设计的可替换加载类的场景,重加载场景OSGI JSP 等,否则是很难达成的。

  3. 该类对应的 java.lang.Class 对象没有被任何地方引用,无法在任何地方通过反射的方式访问该类的方法。