자바 가상 머신 과정의 스크린 샷에 대한 심층적 이해 -5-Mr. Song Hongkang

[ From B station video : 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 가상 머신 레벨의 메모리 분할입니다 . 우리의 애플리케이션은 모두 자바 가상 머신에서 실행되고 있습니다.

[인터뷰 질문 : 메모리 누수 사례 : (1) 싱글 톤 모드 (2) 클로즈를 제공하는 일부 리소스가 닫히지 않아 리소스 누수 발생]

[ 메모리 누수 란 무엇입니까? 메모리 누수는 일부 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();
    }
}

    

[Garbage Collection Thread 및 User Thread : 사용자 스레드는 어느 시점에서든 가비지 수집 스레드가 가비지 수집 스레드를 중지하고 수집하도록 할 수 있으며 특정 위치에서만 사용자 스레드를 일시적으로 중지하고 GC로 이동할 수 있습니다. 이러한 특정 위치는 "안전 지점" 입니다. 예 : 예를 들어 고속도로에서 운전할 때 고속도로의 어떤 위치에서도 마음대로 정지 할 수 없습니다. 서비스 영역에서만 정지 및 휴식이 허용됩니다. 이러한 서비스 영역은 안전 지점의 존재와 유사합니다 .

[인터뷰 질문 : GC가 발생했을 때 모든 쓰레드가 가장 가까운 안전한 지점에서 멈췄는지 확인하는 방법?

  

[참고 :이 장에서 언급 한  4 개의 strong, weak, weak 참조는 모두 오브젝트에 도달 할 수있을 때 설명되며 오브젝트에 도달 할 수없는 경우 직접 재활용됩니다.

[참고 :이 장에서 언급 한  4 개의 strong, weak, weak 참조는 모두 오브젝트에 도달 할 수있을 때 설명되며 오브젝트에 도달 할 수없는 경우 직접 재활용됩니다.

[첫 번째 가비지 수집 : 터치 할 수없는 자바 객체 (또는 도달 할 수없는 자바 객체)가 재활용되는 프로세스를 나타냅니다. 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 개의 strong, weak, weak 참조는 모두 오브젝트에 도달 할 수있을 때 설명되며 오브젝트에 도달 할 수없는 경우 직접 재활용됩니다.

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 개의 strong, weak, weak 참조는 모두 오브젝트에 도달 할 수있을 때 설명되며 오브젝트에 도달 할 수없는 경우 직접 재활용됩니다.

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