安全点和安全区域

前面介绍了垃圾回收器几个方面的内容:

  • 如何标记垃圾
  • 如何处理垃圾

那么还有什么问题要解决呢?

既然是自动垃圾回收,那么自动是什么情况呢?

在我看来,自动主要来自两个方面:

  • 当我们年轻代、老年代内存不足时,触发某种条件,进行垃圾收集
  • 还有一个就是我们接下来说的安全点、安全区域

安全点

为什么要有安全点呢?

在一个程序执行过程中,我们不一定要等到内存不足的时候,再进行垃圾回收整理。

如果只是等待到内存不足的时候再进行整理,那么这个时候,就会有大量的垃圾,那么处理起来,要耗费的时间就会增长。

所以,为了减少这种行为,平时的时候就可以进行收集。可以看下下面这个程序。

package GC;

import java.util.ArrayList;

public class SafePoint {
    public static void main(String[] args) {
        while(true){
            ArrayList list = new ArrayList<>();
            for (int i = 0; i < 1000; i++) {
                list.add(i);
            }
        }
    }
}

这个程序,每次一个新的循环的时候,之前的list引用就指向了新的引用。我们设置 JVM 运行参数为:

-XX:+PrintGCDetails -Xms10m -Xmx10m

设置打印 GC 收集的过程,以及堆的大小为 10M。

可以看到,垃圾回收器并没有等到内存不足的时候再去清理。而是在 2M 左右的时候,就去进行了清理。再清理之后,内存占用只有 1M 不到。

像这种,在程序的某个点进行垃圾回收的,这个点就是安全点。

在垃圾回收的过程中,如果程序仍在运行,有可能使得对象的引用发生改变,那么也许我们上一秒标记的存活对象,这一秒就是死亡对象了。

所以,我们相对而言,要寻找那种,让程序运行比较耗时的指令出,进行安全点的设置。进而允许垃圾回收,比如方法调用,循环跳转,异常跳转等。

安全点与垃圾回收

注意:程序运行到安全点,不是一定要进行垃圾回收。而是在这些点上进行垃圾回收,较为安全。所以叫做安全点。

如何在垃圾收集发生时,让所有线程到达最近的安全点?

  • 抢先式中断:发生垃圾回收时,所有的线程都中断,如果有的线程不在安全点,那么恢复一会,再中断,直到到达安全点
  • 主动式中断:如果有线程到达安全点,会去主动询问一个标识位,这个标志位说明,要不要发生中断。如果需要发生中断,那么这个线程就会中断自己,等到垃圾回收。

抢先式中断,会造成大量的线程切换,浪费大量的时间,所以几乎不使用。更多的是使用 主动式中断。

安全区域

我们上面说了,线程运行时的状态,寻找安全点进行询问是否垃圾回收。

那么如果线程处于 睡眠 或者 阻塞 状态,那么怎么办?这个时候,他就无法进行询问标志位了。

如果在线程进入这些状态前,标示自己可以随时被 垃圾回收器 进行相关对象的标记即可。

其实,这就是安全区域,因为一旦线程进入这些状态,那么其内对象不会发生改变,所以也就是随时可以被垃圾回收器进行处理。

如果当线程重新启动后,发生垃圾回收器还没处理完,那么就继续等待。如果垃圾回收器没有处理或者已经处理完了,那么就继续执行。

安全区域,无非就是另一个告诉垃圾回收器,你可以处理我的方式。

package GC;

public class SafeRegion {
    public static void main(String[] args) throws InterruptedException {
        byte[] bigSize = new byte[10 * 1024 * 1024];
        bigSize = null;
        new Thread(()->{
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.gc();
        }).start();
        Thread.sleep(10000);
    }
}

可以看到,主线程已经进入睡眠状态,这个时候,再另一个线程中,调用垃圾回收,仍然可以对垃圾进行清理。

你可能感兴趣的:(安全点和安全区域)