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对象没有在任何地方被引用,无法通过反射访问该类的方法
只是被允许回收但不是必然回收