概述
java内存运行时 程序计数器 虚拟机栈 本地方法栈三个区域随线程生或灭 栈中的栈帧随方法的进入和退出而有条不紊的执行着出栈和入栈 因此这几个区域的内存分配和回收都具备确定性
而java堆和方法区这两个区域则有不确定性: 一个接口的多个实现类需要的内存不同 一个方法所执行的不同条件分支所需要的内存也不同 这部分内存的分配和回收是动态的 垃圾收集器所关注的也是这方面内存如何管理
堆里存放着java中所有对象的实例 垃圾收集器对堆进行回收前 需要确定哪些对象还“活着” 哪些“死去”(即不可能在被任何途径使用的对象)
可达性分析来判定对象是否存活
通过一系列称为“GC Roots”的根对象作为起始节点集 从这些节点开始根据引用关系向下搜索 过程中所走的路径称为“引用链” 如果某个对象到GC Roots间没有任何引用链相连 即从GC Roots到该对象不可达 证明该对象不可能在被使用
固定可作为GC Roots对象的:
- 虚拟机栈(栈帧中的本地变量表)引用的对象 如各个线程被调用的方法堆栈中的参数 局部变量等
- 在方法区中类静态属性引用的对象 如java类的引用类型静态变量
- 方法区中常量引用的对象
- 本地方法栈中JNI引用的对象
- jvm内部的引用
- 所有被同步锁(synchronized关键字)持有的对象
- 反应jvm内部情况的JMXBean JVMTI中注册的回调 本地代码缓存等
引用
jdk1.2以后引用分为强引用 软引用 弱引用 虚引用四种
强引用: 最传统的引用的定义 指在程序代码中普遍存在的引用赋值 类似“Object obj = new Object()” 无论在任何情况下 只要强引用关系还在 垃圾收集器就永远不会回收掉该对象
软引用:描述一些还有用 但是非必须的对象。jdk1.2以后提供了SoftReference类来实现软引用 只被软引用关联着的对象 在系统将要发生内存溢出异常前 会把这些对象列进回收范围中进行第二次回收 若内存还不够则抛出内存溢出异常
弱引用: 也是描述非必须对象 但强度比软引用更弱一些 jdk1.2以后提供了WeakReference类实现弱引用 被弱引用关联的对象只能生存到下一次垃圾收集发生为止 垃圾收集器开始工作时无论内存是否足够都会回收只被弱引用关联的对象
虚引用:也称“幽灵引用”或“幻影引用” 最弱的引用关系 一个对象是否有虚引用的存在完全不影响其生存时间 也无法通过一个虚引用来取得一个对象实例 jdk1.2以后提供了PhantomReference类来实现虚引用 存在目的:为了能在该对象被收集器回收时收到一个系统通知
对象的死亡
即便被可达性分析算法判定为不可达的对象 也不是“非死不可” 此时他处于“缓刑状态” 要宣告一个对象的死亡 需要至少经历两次标记过程:
1. 可达性分析判定
2. 筛选此对象是否有必要执行finalize()方法 若对象没有覆盖finalize()方法或者该方法已经被jvm调用过 则视为“没有必要执行” (非标记)
若被判定为有必要执行finalize()(注意: 任何对象的finalize()方法都只会执行一次) 放入F-Queue队列中 由一条jvm自动创建的 低调低优先级的Finalizer线程去执行其finalize()方法(jvm只触发该方法的执行 不去等待 防止finalize()执行缓慢 造成死循环) 收集器将对F-Queue中的对象进行二次标记 而在队列中 该对象仍有机会逃脱 只要重新与引用链上任意一个对象建立关联 则被移除
注意:尽量避免使用finalize() 运行代价高 不确定性大 被声明为不推荐使用的语法