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)クローズを提供する一部のリソースがクローズされず、リソースリークが発生します]

[メモリリークとは:メモリリークとは、一部の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が発生したときにすべてのスレッドが最も近い安全なポイントで停止したことを確認するにはどうすればよいですか?

  

[注:この章で説明されている 4つの強い、弱い、弱い参照はすべて、オブジェクトに到達できるときに説明されます。オブジェクトに到達できない場合は、直接リサイクルされます。

[注:この章で説明されている 4つの強い、弱い、弱い参照はすべて、オブジェクトに到達できるときに説明されます。オブジェクトに到達できない場合は、直接リサイクルされます。

[最初のガベージコレクション:タッチできない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之前,垃圾回收器会回收软引用的可达对象。
        }
    }
}

[注:この章で説明されている 4つの強い、弱い、弱い参照はすべて、オブジェクトに到達できるときに説明されます。オブジェクトに到達できない場合は、直接リサイクルされます。

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());
    }
}

[注:この章で説明されている 4つの強い、弱い、弱い参照はすべて、オブジェクトに到達できるときに説明されます。オブジェクトに到達できない場合は、直接リサイクルされます。

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