弄懂垃圾回收需要了解3个问题,第一,回收发生在哪里?第二,对象在上面时候可以被回收?第三,如何回收这些对象?
1、回收发生在哪里 jvm内存区域中,程序计数器,虚拟机栈和本地方法栈这三个区域是有线程私有的,随着线程的创建而创建,销毁而销毁;栈中的栈帧随着方法的进入和退出而进行入栈和出栈的操作,每个栈帧中分配的多少内存在类结构确定的时候就已经知道的,因此这3个区块的内存分配和回收都具有确定性。
2、对象在思明时候可以被回收? jvm如何判断对象是可以被回收的?一个ui想不再被引用,就表示该对象可以被回收。目前有2种算法可以判断对象是否可以被回收。
引用计数法:通过一个对象的引用计数器来判断该对象是否被引用了。当对象被引用,引用计数器就会加1,当引用失效,计数器减1,当对象的引用计数器为0时,就表示该对象不再被引用,可以被回收。计数算法实现简单,效率也高,但是存在对象之间相互循环引用的问题。
什么是对象之间相互循环引用?看下面的图片

可达性分析算法:GC Roots是该算法的基础,GC Roots是所有对象的根对象,在JVM加载的时候,会创建一些普通对象引用正常对象。这些对象作为正常对象的起始点,在垃圾回收时,会从这些GC Roots向下搜索,当一个对象到GC Roots没有任何引用连相连接时,就正常此对象是不可用的。

GC Roots对象:
虚拟机栈(栈帧中的本地变量表)中引用的对象
方法区中静态属性引用的对象
方法区中常量引用的对象
本地方法栈中JNI引用的对象
标记算法,在可达性分析算法中不可达的对象,暂时处于待回收状态,一个对象真正被回收,要经过两次标记过程。
第一次标记并进行一次筛选。筛选的条件是此对象是否有必要执行finalize()方法。当对象没有覆盖此方法或者此方法已经被虚拟机调用过,虚拟机将这两张情况都视为“没有必要执行”,对象被回收
第二次标记,如果对象被判定为有必要执行finalize()方法,那么该对象会被放置在一个名为:F-Queue的队列之中,并在稍后由一条虚拟机自动建立的、低优先级的Finalizer线程去执行。这里所谓的“执行”是指虚拟机会触发这个方法,但是并不承诺会等待它运行结束。这样的原因是,如果一个对象finalizer()方法中执行缓慢,或者发生死循环,将很可能会导致F-Queue队列中的其他对象永久处于等待状态,甚至导致整个内存回收系统崩溃。Finalize()方法是对象逃脱死亡命运的最后一次机会,稍后GC将对F-Queue中的对象进行第二次小规模标记,如果对象要在finalize()中拯救自己,只需要中心将引用连上的任何一个对象建立关联即可,那将被暂时移除“即将回收”的集合。如果对象还没有建立连接,对象基本被回收。
Java中的引用分为以下4种:

3、对象如何回收,遵循两个特性
自动性:Java提供一个系统级别的线程来追踪每一块分配出去的内存空间,当JVM处于空闲循环时,垃圾收集器线程会自动检查每一块分配出去的内存空间,然后自动回收每一块空间的内存块。
不可预期性:一但一个对象没有被引用了,该对象是否立刻被回收?很难确定一个没有被引用的对象是不是会立刻回收,因为有可能程序结束的时候,对象仍然在内存中。垃圾回收线程在JVM中自动执行的,Java程序无法强制执行。
GC算法,JVM提供了不同的回收算法来实现这套机制,有如下几种算法:

分析以下代码:
public class GcTest {
public static GcTest text = null;
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("finalize method execute");
}
public void isAlive(){
System.out.println("yes i am alive");
}
public static void main(String[] args) throws InterruptedException {
text = new GcTest();
//第一次GC
text = null;
System.gc();
//gc比较慢,需要延时执行
Thread.sleep(500);
if(text !=null){
text.isAlive();
}else {
System.out.println("i am died");
}
//第二次GC
text = null;
System.gc();
//gc比较慢,需要延时执行
Thread.sleep(500);
if(text !=null){
text.isAlive();
}else {
System.out.println("i am died");
}
}
}
运行结果:
finalize method execute
yes i am alive
i am died
从上面的代码可以看出,text对象确实自救过一次,并且在收集前成功逃脱了。另外有一点是,两端一样的代码,第一次成功逃脱,第二次却失败了,这是因为任何一个对象的finalize()方法都只会被系统自动调用一次,如果有下一次回收,finalize方法不会再被执行,因此第二次自救失败。