线程私有的程序计数器、虚拟机栈和本地方法栈随线程而生,随线程而灭。栈中的栈帧随方法的进入和退出有条不紊的入栈和出栈。
而Java堆和方法区因为需要多大内存、创建多少对象都是不确定的,因此这两个区域是垃圾回收的重点对象。
一、如何判断对象是否存活
1. 引用计数法
给每个对象添加一个引用计数器,有一个地方引用就+1,引用失效就-1。计数器为0,即该对象无法被访问。
优点:实现简单,判断效率高。
缺点:难以解决对象之间的循环引用问题。
2. 可达性分析法
通过一系列“GC Roots”的对象作为起始点,向下搜索。判断是否可以通过"GC Roots"对象的引用链访问到对象。
可以作为“GC Roots”的对象:
- 虚拟机栈(栈帧中的本地变量表)中引用的对象
- 方法区中类静态属性引用的对象
- 方法区中常量引用的对象
- 本地方法栈中引用的对象
二、 Java中的4中不同程度的引用
强引用:new出的。只要强引用还在,永远不会被垃圾回收。
软引用:SoftRefference类来实现。系统将要发生内存溢出异常之前,将这些对象进行二次回收。
弱引用:WeakRefference类来实现。只能生存到下一个垃圾回收期。无论内存是否充足,都会回收。
虚引用:PhantomRefference类来实现。
三、 回收方法区
“废弃常量”
没有任何String对象引用常量池中的“abc”常量,也没有其他地方引用了这个字面量。
“无用的类”
- 该类的所有实例都已被回收
- 加载该类的ClassLoader已经被回收
- 该类的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
四、垃圾收集算法
1. 标记 — 清除算法
首先标记出所有需要回收的对象
然后对标记完成的对象统一进行回收
缺点: 内存分配的效率很低,产生大量的内存碎片。
2. 复制算法
将内存分为两块,每次使用其中一块。垃圾回收时,将存活的对象复制到另一块上面,然后清理已使用过的内存空间。
优点:内存分配效率高,实现简单,运行高效。
缺点:内存浪费严重。
改进:新生代使用Eden,from,to( 8 : 1 : 1)
3. 标记 — 整理算法
先对所有对象进行标记,然后让存活对象都向一端移动。
4.分代收集算法
新生代使用 复制 算法
老年代使用 标记 — 整理 算法