对象已死指不可能再被任何途径使用的对象。

  垃圾收集器主要关注3个问题:哪些内存需要回收?什么时候回收?如何回收?本篇就是对第一个问题的回答。

一、回收的区域

  程序计数器、虚拟机栈、本地方法栈3个区域随线程而生,随线程而灭,内存多少根据类结构是已知的,内存分配和回收具备确定性。

  Java堆和方法区两个区域不确定性。只有处于运行期间,我们才能知道程序究竟会创建哪些对象,多少个对象,这部分内存的分配和回收是动态的。垃圾收集器所关注的正是这部分内存该如何管理。

二、判断对象已死的算法

2.1 引用计数算法

  在对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加一;当引用失效时,计数器就减一;任何时刻计数器为零的对象就是不可能再被使用的。

  原理简单,判断效率也很高。但需要配合大量额外的处理才能保证工作,比如无法解决对象之间相互循环引用的问题。 比如:对象A和对象B都有字段instance,赋值令A.instance=B, B.instance=A,除此之外,再出引用,但它们互相引用着,计数不为零,如果用引用计数算法,则无法回收。

2.2 可达性分析算法

  通过一系列称为“GC Roots”的根对象作为起始节点集,从这些节点开始,根据引用关系向下搜索,搜索过程所走过的路径称为“引用链”(Reference Chain),如果某个对象到GC Roots间没有任何引用链相连,则说明此对象不可能再被使用。

监控java运行到哪步 java监控进程是否死掉_java

  在Java技术体系里面,固定可作为GC Roots的对象包括以下几种:

  • 在虚拟机栈(栈帧中的本地变量表)中引用的对象,譬如各个线程被调用的方法堆栈中使用到的 参数、局部变量、临时变量等。
  • 在方法区中类静态属性引用的对象,譬如Java类的引用类型静态变量。
  • 在方法区中常量引用的对象,譬如字符串常量池(String Table)里的引用。·在本地方法栈中JNI(即通常所说的Native方法)引用的对象。
  • Java虚拟机内部的引用,如基本数据类型对应的Class对象,一些常驻的异常对象(比如 NullPointExcepiton、OutOfMemoryError)等,还有系统类加载器。
  • 所有被同步锁(synchronized关键字)持有的对象。
  • 反映Java虚拟机内部情况的JMXBean、JVMTI中注册的回调、本地代码缓存等。

三、引用分类

  当内存空间还足够时,能保留在内存之中,如果内存空间在进行垃圾收集后仍然非常紧张,那就可以抛弃这些对象,类似缓存功能遇到的场景。

  引用分类有:

  • 强引用:类似“Object obj = new Object()”这种引用关系,无论任何情况,垃圾收集器都不回收。
  • 软引用:还有用,但没必须的对象。JDK1.2提供SoftReference类实现。将要发生内存溢出前,这些对象列入第二次的回收范围。
  • 弱引用:也是非必须对象,比软引用更弱。JDK1.2提供WeakReference类实现。当垃圾收集器开始工作,无论内存是否足够,都回收。
  • 虚引用:为一个对象设置虚引用的唯一目的是为了能在这个对象被收集器回收时收到一个系统通知。JDK1.2提供PhantomReference类实现。

四、回收方法区

  方法区的垃圾收集主要两部分:废弃的常量和不再使用的类型。

  • 废弃的常量:没有任务对象引用的常量池中的常量。
  • 不再被使用的类型: 同时满足下面3个条件:

  1、该类所有的实例都已经被回收,也就是Java堆中不存在该类及其任何派生子类的实例。

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

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

  Java虚拟机被允许对满足上述三个条件的无用类进行回收,这里说的仅仅是“被允许”,而并不是和常量一样,没有引用了就必然会回收。关于是否要对类型进行回收, 还要根据- Xnoclassgc参数进行控制。

  另外还可以指定参数查看类加载和卸载信息:

  • -verbose:class 和 -XX:+TraceClassLoading 可以在 Product版的虚拟机中使用。
  • -XX: +TraceClassUnLoading 可以在FastDebug版的虚拟机使用。

五、小结

  本篇介绍了Java虚拟机判断对象已死的机制,以及两个分析算法:引用计数算法、可达性分析算法。其中,被大量应用的算法是可达性分析算法,HotSpot虚拟机也是使用该算法。