深入理解java虚拟机课程的截图-5-宋红康老师

来自B站视频:https://www.bilibili.com/video/BV1BJ41177cp?p=154

【强软弱虚引用:是比较偏门的高频面试题。哎。】

【测试结果:仅仅显示调用System.gc()方法时 ,发现SystemGCTes类重写的finaliize方法有时会调用,有时不会调用,这就说明了System.gc()方法的确有这么一个免责声明:它无法保证一定对垃圾收集器的调用。】

//测试不同情况下的GC。结果很妙,可以亲自测试下。-XX:PrintGCDetails
public class LocalVarGC {
    public void localvarGC1() {
        byte[] buffer = new byte[10 * 1024 * 1024];//10MB
        System.gc();
    }
    public void localvarGC2() {
        byte[] buffer = new byte[10 * 1024 * 1024];
        buffer = null;
        System.gc();
    }
    public void localvarGC3() {
        {
            byte[] buffer = new byte[10 * 1024 * 1024];
        }
        System.gc();
    }
    public void localvarGC4() {
        {
            byte[] buffer = new byte[10 * 1024 * 1024];
        }
        int value = 10;
        System.gc();
    }
    public void localvarGC5() {
        localvarGC1();
        System.gc();
    }
    public static void main(String[] args) {
        LocalVarGC local = new LocalVarGC();
        local.localvarGC5();//这里改变为不同的方法调用
    }
}

【随着GC一直在迭代发展,所以一般情况下,除非应用程序占用的内存增长速度非常快,造成垃圾回收已经跟不上内存消耗的速度了,否则不太容易出现OOM的情况。 报OOM的场景是:想要给新对象分配内存,但是,JVM没有足够多的空闲内存了,并且进行垃圾收集之后也无法提供足够的空闲内存,此时,就会报出OOM错误。】

【什么堆啊、栈啊、方法区啊 等这些内存空间的划分,都是java虚拟机层面的内存划分,被称之为虚拟内存。要知道,我们的应用程序都是运行在java虚拟机中的。】

【面试题:内存泄漏的案例:(1)单例模式(2)一些提供close的资源未关闭导致资源泄漏】

什么是内存泄漏:内存泄漏是指,一些java对象在应用程序中已经不再使用了,但是,由于应用程序中其他的一些还在使用的java引用仍然和它们保持着引用关系,也即 这些java对象虽然不再使用,但是仍然存在着从GCRoots到它们的引用链,导致垃圾收集器没有办法回收它们,这就是导致了内存泄漏。你能举出内存泄漏的例子吗?(1)在引用计数算法中,循环引用的情况就是内存泄漏的例子。但是,在java中,并不是使用引用计数算法来标识垃圾对象的,而是使用可达性分析算法来标识可达对象的。所以,前面说的循环引用的例子得有个前提,就是在引用计数算法的前提下。(2)在可达性分析算法的场景下,内存泄漏的例子是:(2.1)比如 单例模式,单例的生命周期和应用程序一样长,所以在单例程序中,如果持有外部对象的引用的话,那么这个外部对象是不能够被回收的,就会出现内存泄漏的产生。(2.2)还比如 有一些提供了close()方法的资源未及时关闭导致的内存泄漏的情况,比如,IO连接、数据库连接等,这些资源使用完之后需要及时释放掉,否则也会造成内存泄漏。】

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

//用来感受下STW事件的存在!
public class StopTheWorldDemo {
    public static class WorkThread extends Thread {
        List<byte[]> list = new ArrayList<byte[]>();
        public void run() {
            try {
                while (true) {
                    for(int i = 0;i < 1000;i++){
                        byte[] buffer = new byte[1024];
                        list.add(buffer);
                    }
                    if(list.size() > 10000){
                        list.clear();
                        System.gc(); //会触发full gc,进而会出现STW事件
                    }
                }
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    }

    public static class PrintThread extends Thread {
        public final long startTime = System.currentTimeMillis();
        public void run() {
            try {
                while (true) {
                    // 每秒打印时间信息
                    long t = System.currentTimeMillis() - startTime;
                    System.out.println(t / 1000 + "." + t % 1000);
                    Thread.sleep(1000);
                }
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        WorkThread w = new WorkThread();
        PrintThread p = new PrintThread();
        w.start(); //两种测试场景:(1)注释掉测试。(2)不注释掉测试。
        p.start();
    }
}

    

【垃圾回收线程与用户线程:并不是在任何时间点 用户线程都可以停顿下来 让垃圾回收线程去回收垃圾,只有在一些特定的位置才能够让用户线程暂时停下来 让垃圾回收线程去GC,那这些特定的位置就是“安全点”。举例:比如在高速公路上开车,不能很随意的在高速公路上的任何位置都允许你停下来,只有在服务区才允许停下来休息,这些服务区就类似于安全点的存在

【面试题:如何在GC发生时,检查所有线程都跑到最近的安全点停顿下来呢?】

  

【注:本章说的 强软弱虚 四大引用,都是在对象可达的情况下说明的,如果要是对象不可达,那就直接被回收掉了。】

【注:本章说的 强软弱虚 四大引用,都是在对象可达的情况下说明的,如果要是对象不可达,那就直接被回收掉了。】

【第一次垃圾回收:是指不可触及的java对象(或者说不可达的java对象)被回收的过程。OOM与“非强引用”没有关系,因为在即将OOM的时候,“非强引用”都会被回收,我都被回收了,JVM仍然出现OOM,那这个OOM跟我有毛线关系啊。】

import java.lang.ref.SoftReference;
//软引用的测试:内存不足即回收。-Xms10m -Xmx10m -XX:+PrintGCDetails
public class SoftReferenceTest {
    public static class User {
        public User(int id, String name) {
            this.id = id;
            this.name = name;
        }
        public int id;
        public String name;
        @Override
        public String toString() {
            return "[id=" + id + ", name=" + name + "] ";
        }
    }

    public static void main(String[] args) {
        //创建对象,建立软引用
//        SoftReference<User> userSoftRef = new SoftReference<User>(new User(1, "songhk"));
        //上面的一行代码,等价于如下的三行代码
        User u1 = new User(1,"songhk");
        SoftReference<User> userSoftRef = new SoftReference<User>(u1);
        u1 = null;//一定要有的代码:取消强引用

        //从软引用中重新获得强引用对象
        System.out.println(userSoftRef.get());

        System.gc();
        System.out.println("After GC:");
//        //垃圾回收之后获得软引用中的对象
        System.out.println(userSoftRef.get());//由于堆空间内存足够,所有不会回收软引用的可达对象。
//
        try {
            //让系统认为内存资源紧张、不够
//            byte[] b = new byte[1024 * 1024 * 7];//让系统认为内存资源不够
            byte[] b = new byte[1024 * 7168 - 635 * 1024];//让系统认为内存资源紧张
        } catch (Throwable e) {
            e.printStackTrace();
        } finally {
            //再次从软引用中获取数据
            System.out.println(userSoftRef.get());//在报OOM之前,垃圾回收器会回收软引用的可达对象。
        }
    }
}

【注:本章说的 强软弱虚 四大引用,都是在对象可达的情况下说明的,如果要是对象不可达,那就直接被回收掉了。】

import java.lang.ref.WeakReference;
//弱引用的测试:发现即回收
public class WeakReferenceTest {
    public static class User {
        public User(int id, String name) {
            this.id = id;
            this.name = name;
        }
        public int id;
        public String name;
        @Override
        public String toString() {
            return "[id=" + id + ", name=" + name + "] ";
        }
    }

    public static void main(String[] args) {
        //构造了弱引用
        WeakReference<User> userWeakRef = new WeakReference<User>(new User(1, "songhk"));
        //从弱引用中重新获取对象
        System.out.println(userWeakRef.get());

        System.gc();
        // 不管当前内存空间足够与否,都会回收它的内存
        System.out.println("After GC:");
        //重新尝试从弱引用中获取对象
        System.out.println(userWeakRef.get());
    }
}

【注:本章说的 强软弱虚 四大引用,都是在对象可达的情况下说明的,如果要是对象不可达,那就直接被回收掉了。】

import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
//虚引用的测试:对垃圾回收没有任何影响。
//给对象设置一个虚引用关联关系的唯一目的:就是在对象垃圾回收器回收之时收到一个系统通知。
public class PhantomReferenceTest {
    public static PhantomReferenceTest obj;//声明一个当前类的对象obj
    static ReferenceQueue<PhantomReferenceTest> phantomQueue = null;//引用队列
    public static class CheckRefQueue extends Thread {
        @Override
        public void run() {
            while (true) {
                if (phantomQueue != null) {
                    PhantomReference<PhantomReferenceTest> objt = null;
                    try {
                        objt = (PhantomReference<PhantomReferenceTest>) phantomQueue.remove();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    if (objt != null) {
                        System.out.println("追踪垃圾回收过程:PhantomReferenceTest实例被GC了");
                    }
                }
            }
        }
    }

    @Override
    protected void finalize() throws Throwable { //finalize()方法只能被调用一次!
        super.finalize();
        System.out.println("调用当前类的finalize()方法");
        obj = this; //使当前对象复活
    }

    public static void main(String[] args) {
        Thread t = new CheckRefQueue();
        t.setDaemon(true);//设置为守护线程:当程序中没有非守护线程时,守护线程也就执行结束。
        t.start();

        phantomQueue = new ReferenceQueue<PhantomReferenceTest>();
        obj = new PhantomReferenceTest(); //obj是个强引用
        //构造了 PhantomReferenceTest 对象的虚引用,并指定了引用队列
        //使得obj这个强引用关联一个虚引用phantomRef
        PhantomReference<PhantomReferenceTest> phantomRef = new PhantomReference<PhantomReferenceTest>(obj, phantomQueue);

        try {
            //不可获取虚引用中的对象:返回null
            System.out.println(phantomRef.get());

            obj = null;//将强引用去除
            //第一次进行GC,由于obj这个强引用对象在finalize()方法中复活了,所以GC无法回收它
            System.gc(); //第一次GC
            Thread.sleep(1000); //由于GC线程优先级较低,这句话是为了让GC线程执行一下
            if (obj == null) {
                System.out.println("obj 是 null");
            } else {
                System.out.println("obj 可用");
            }

            System.out.println("第 2 次 gc");
            obj = null;//将强引用去除
            System.gc(); //第二次GC,一旦将obj对象回收,就会将此虚引用存放到引用队列中
            Thread.sleep(1000);
            if (obj == null) {
                System.out.println("obj 是 null");
            } else {
                System.out.println("obj 可用");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

猜你喜欢

转载自blog.csdn.net/cmm0401/article/details/108908310