深入理解Java虚拟机:第三章

165 阅读3分钟

03、垃圾收集器与内存分配策略

概述与对象已死?

概述:哪些内存需要回收、什么时候回收、如何回收

虚拟机栈、本地方法栈、程序计数器同线程同生同死

栈帧随方法的进出有条不紊的入栈出栈

内存的分配在编译期间就确定了

Java堆和方法区有显著的不确定性:

  • 一个接口多个实现类需要的内存不一样
  • 一个方法的不同实现需要的内存也不一样

引用计数算法

在对象中添加一个引用计数器,被引用时加一;引用失效减一;引用计数器为零时就是不被使用的(但是Java虚拟机使用的不是这个)

可达性分析算法

通过一系列称为GCRoots的根对象根据引用关系向下搜索,如果没有引用链说明对象不可使用可以回收

称为GCRoots有:

  • 虚拟机栈中引用的对象,比如各个线程方法堆栈使用的参数、局部变量、临时变量
  • 方法区中类静态属性引用的对象
  • 方法区中常量引用的对象,比如字符串常量池
  • 本地方法栈中引用的对象
  • 虚拟机内部的引用,如基本数据类型对应的Class对象,一些常驻的异常对象还有系统类加载器
  • 所有被同步锁持有的对象
  • 反映Java虚拟机内部情况的JMXBean、JVMTI中注册回调、本地代码缓存。
  • 除了以上固定的还有其他对象可以“临时”加入,比如分带收集、局部回收

引用

  • 强引用:如Object obj = new Object();,只要该关系存在就不会被回收
  • 软引用:还有用但是不是必须的对象,在系统内存溢出前会被加入将要回收的集合中进行第二次回收,如果回收了还是内存不够会抛出OOM
  • 弱引用:比软引用还弱,存活在下次回收这段时间内,如果系统决定回收属于必死的
  • 虚引用:为了对象在回收时能收到一个系统信息

生存还是死亡

即使在可达性分析算法中判定为不可达的对象,也不是非死不可。这里面有两次标记:

  • 在进行可达性分析后发现没有与GCRoots相连接的引用链,将会第一次被标记,随后进行筛选

    • 筛选条件是:此对象有没有必要执行finalize()方法。
    • 假如对象没有覆盖finalize()方法
    • 或者finalize()方法已经被虚拟机调用过,被视为没有必要执行
  • 如果被判定为有必要执行finalize()方法,那么对象会被放置在一个为F-Queue队列之中,并在随后由一条虚拟机自动创建的、低调度优先级的Finalizer的线程去执行finalize()方法

  • finalize()方法是对象逃脱死亡的最后一次机会,因为这个方法有一定的延迟,所以只需要建立一条引用即可存活

注:finalize()方法只会执行一次,如果再次面临回收,finalize()方法不会再次运行,所以该对象被回收

回收方法区

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

废弃的常量就是不再有任何对象引用该常量

不再使用的类:

  • 该类所有的实例都被回收,Java堆中不存在该类以及任何派生子类的实例
  • 加载该类的类加载器已经被回收
  • 该类的java.lang.Class对象没有在任何地方被引用,无法通过反射访问该类的方法

只是被允许回收但不是必然回收