JVM

21-垃圾回收相关概念

2021-06-07  本文已影响0人  紫荆秋雪_文

一、System.gc

实例1

public class SystemGCTest {

    public void localvarGC1() {
        byte[] buffer = new byte[10 * 1024 * 1024]; // 10MB ( 10240k )
        System.gc();
    }

    public static void main(String[] args) {
        SystemGCTest gcTest = new SystemGCTest();
        gcTest.localvarGC2();
    }
}
[GC (System.gc()) [PSYoungGen: 12949K->503K(38400K)] 12949K->10751K(125952K), 0.0218291 secs] [Times: user=0.01 sys=0.00, real=0.02 secs] 
[Full GC (System.gc()) [PSYoungGen: 503K->0K(38400K)] [ParOldGen: 10248K->10683K(87552K)] 10751K->10683K(125952K), [Metaspace: 2937K->2937K(1056768K)], 0.0129399 secs] [Times: user=0.01 sys=0.01, real=0.02 secs] 

实战2

public class SystemGCTest {

    public void localvarGC2() {
        byte[] buffer = new byte[10 * 1024 * 1024]; // 10MB
        buffer = null;
        System.gc();
    }

    public static void main(String[] args) {
        SystemGCTest gcTest = new SystemGCTest();
        gcTest.localvarGC2();
    }
}
[GC (System.gc()) [PSYoungGen: 12949K->567K(38400K)] 12949K->575K(125952K), 0.0012042 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
[Full GC (System.gc()) [PSYoungGen: 567K->0K(38400K)] [ParOldGen: 8K->443K(87552K)] 575K->443K(125952K), [Metaspace: 2936K->2936K(1056768K)], 0.0166308 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 

实战3

public class SystemGCTest {

    public void localvarGC3() {
        {
            byte[] buffer = new byte[10 * 1024 * 1024]; // 10MB
        }
        System.gc();
    }

    public static void main(String[] args) {
        SystemGCTest gcTest = new SystemGCTest();
        gcTest.localvarGC3();
    }
}
[GC (System.gc()) [PSYoungGen: 12949K->535K(38400K)] 12949K->10783K(125952K), 0.0232992 secs] [Times: user=0.01 sys=0.01, real=0.02 secs] 
[Full GC (System.gc()) [PSYoungGen: 535K->0K(38400K)] [ParOldGen: 10248K->10683K(87552K)] 10783K->10683K(125952K), [Metaspace: 2938K->2938K(1056768K)], 0.0095834 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 

实战4

public class SystemGCTest {
    public void localvarGC4() {
        {
            byte[] buffer = new byte[10 * 1024 * 1024]; // 10MB
        }
        int value = 10;
        System.gc();
    }

    public static void main(String[] args) {
        SystemGCTest gcTest = new SystemGCTest();
        gcTest.localvarGC4();
    }
}
[GC (System.gc()) [PSYoungGen: 12949K->503K(38400K)] 12949K->511K(125952K), 0.0030137 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
[Full GC (System.gc()) [PSYoungGen: 503K->0K(38400K)] [ParOldGen: 8K->443K(87552K)] 511K->443K(125952K), [Metaspace: 2937K->2937K(1056768K)], 0.0269859 secs] [Times: user=0.00 sys=0.00, real=0.02 secs] 

实战3字节码

字节码.png 局部变量最大槽数.png
本地局部变量表.png

实战4字节码

字节码.png 局部变量最大槽数.png 局部变量表.png

二、内存溢出 与 内存泄露

1、内存溢出(OOM)
2、内存泄漏(Memory Leak)也称 存储渗漏

三、Stop The World

测试Stop The World


public class StopTheWorldTest {

    public static class PrintThread extends Thread {
        public final long startTime = System.currentTimeMillis();

        @Override
        public void run() {
            super.run();
            while (true) {
                long t = System.currentTimeMillis() - startTime;
                System.out.println(t / 1000 + "." + t % 1000);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void main(String[] args) {
        PrintThread printThread = new PrintThread();
        printThread.start();
    }
}
0.1
1.2
2.6
3.10
4.12
5.14
6.17
7.21
8.22
9.26
10.31
11.38

public class StopTheWorldTest {


    public static class StopTheWorldThread extends Thread {

        List<byte[]> list = new ArrayList<>();

        @Override
        public void run() {
            super.run();
            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();
                }

            }
        }
    }

    public static class PrintThread extends Thread {
        public final long startTime = System.currentTimeMillis();

        @Override
        public void run() {
            super.run();
            while (true) {
                long t = System.currentTimeMillis() - startTime;
                System.out.println(t / 1000 + "." + t % 1000);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void main(String[] args) {
        StopTheWorldThread worldThread = new StopTheWorldThread();
        PrintThread printThread = new PrintThread();
        printThread.start();
        worldThread.start();
    }
}

四、垃圾回收的并行与并发

1、并发(Concurrent)

2、并行(Parallel)

3、并发与并行比较

4、垃圾回收的并发与并行

五、安全点与安全区域

1、安全点

程序执行时并非在所有地方都能停顿下来,开始 GC,只有在特定的位置才能停顿下来开始 GC,这些位置称为 "安全点(Safepoint)"。Safe Point 的选择很重要,如果太少可能导致 GC 等待的时间太长,如果太频繁可能导致运行时的性能问题。大部分指令的执行时间都非常短暂,通常会根据 "是否具有让程序长时间执行的特征" 为标准。如:选择一些执行时间较长的指令作为Safe Point,如方法调用、循环跳转和异常跳转等。

2、安全区

SafePoint 机制保证了程序执行时,在不太长的时间内就会遇到可进入 GC 的SafePoint。但是程序 "不执行" 的时候?例如线程处于 Sleep 状态 或 Blocked 状态,这时候线程无法响应 JVM 的中断请求,"走到安全点"去中断挂起,JVM 也不太可能等待线程被唤醒。对于这种情况,就需要安全区域(Safe Region)来解决。

六、引用

场景:当内存空间还足够时,则能保留在内存中;如果内存空间在进行垃圾收集后还是很紧张,则可以抛弃这些对象。为了满足这样的需求,在JDK1.2之后,Java对引用的概念进行了扩充,将引用分为强引用、软引用、弱引用、虚引用,这4种引用强度依次递减。

1、强引用(Strong Reference)

2、软引用(Soft Reference)


/**
 * 软引用
 */
public class SoftReferenceTest {


    public static void main(String[] args) {
        // 创建软引用
        User user = new User(1, "Raven");
        SoftReference<User> softReference = new SoftReference<User>(user);
        // 取消强引用
        user = null;

        // 从软引用中获取引用对象
        System.out.println(softReference.get());
    }

    public static class User {

        public int id;
        public String name;

        public User(int id, String name) {
            this.id = id;
            this.name = name;
        }

        @Override
        public String toString() {
            return "[ id = " + id + " name = " + name + " ]";
        }
    }


}
public static void main(String[] args) {
        // 创建软引用
        User user = new User(1, "Raven");
        SoftReference<User> softReference = new SoftReference<User>(user);
        // 取消强引用
        user = null;

        // 从软引用中获取引用对象
        System.out.println(softReference.get());
    }
[ id = 1 name = Raven ]

    public static void main(String[] args) {
        // 创建软引用
        User user = new User(1, "Raven");
        SoftReference<User> softReference = new SoftReference<User>(user);
        // 取消强引用
        user = null;

        // 从软引用中获取引用对象
        System.out.println(softReference.get());

        // 手动触发 GC
        System.gc();
        System.out.println("GC 之后获取软引用对象:");
        System.out.println(softReference.get());
    }
[ id = 1 name = Raven ]
GC 之后获取软引用对象:
[ id = 1 name = Raven ]

    public static void main(String[] args) {
        // 创建软引用
        User user = new User(1, "Raven");
        SoftReference<User> softReference = new SoftReference<User>(user);
        // 取消强引用
        user = null;

        // 从软引用中获取引用对象
        System.out.println(softReference.get());

        // 手动触发 GC
        System.gc();
        System.out.println("GC 之后获取软引用对象:");
        System.out.println(softReference.get());

        try {

            byte[] bytes = new byte[1024 * 1024 * 7];
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        finally {

            System.out.println("内存紧张 之后获取软引用对象:");
            System.out.println(softReference.get());
        }

    }
[ id = 1 name = Raven ]
GC 之后获取软引用对象:
[ id = 1 name = Raven ]
内存紧张 之后获取软引用对象:
[ id = 1 name = Raven ]
-Xms10m -Xmx10m -XX:+PrintGCDetails
[ id = 1 name = Raven ]
[GC (System.gc()) [PSYoungGen: 1797K->503K(2560K)] 1797K->539K(9728K), 0.0129384 secs] [Times: user=0.00 sys=0.00, real=0.02 secs] 
[Full GC (System.gc()) [PSYoungGen: 503K->0K(2560K)] [ParOldGen: 36K->444K(7168K)] 539K->444K(9728K), [Metaspace: 2942K->2942K(1056768K)], 0.0174918 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
GC 之后获取软引用对象:
[ id = 1 name = Raven ]
[GC (Allocation Failure) [PSYoungGen: 80K->64K(2560K)] 525K->508K(9728K), 0.0109678 secs] [Times: user=0.00 sys=0.00, real=0.02 secs] 
[GC (Allocation Failure) [PSYoungGen: 64K->32K(2560K)] 508K->476K(9728K), 0.0005738 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Allocation Failure) [PSYoungGen: 32K->0K(2560K)] [ParOldGen: 444K->394K(7168K)] 476K->394K(9728K), [Metaspace: 2942K->2942K(1056768K)], 0.0127483 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
[GC (Allocation Failure) [PSYoungGen: 0K->0K(2560K)] 394K->394K(9728K), 0.0004207 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Allocation Failure) [PSYoungGen: 0K->0K(2560K)] [ParOldGen: 394K->378K(7168K)] 394K->378K(9728K), [Metaspace: 2942K->2942K(1056768K)], 0.0132753 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
内存紧张 之后获取软引用对象:
null
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at com.lkty.reference.SoftReferenceTest.main(SoftReferenceTest.java:29)
Heap
 PSYoungGen      total 2560K, used 101K [0x00000007bfd00000, 0x00000007c0000000, 0x00000007c0000000)
  eden space 2048K, 4% used [0x00000007bfd00000,0x00000007bfd19738,0x00000007bff00000)
  from space 512K, 0% used [0x00000007bff80000,0x00000007bff80000,0x00000007c0000000)
  to   space 512K, 0% used [0x00000007bff00000,0x00000007bff00000,0x00000007bff80000)
 ParOldGen       total 7168K, used 378K [0x00000007bf600000, 0x00000007bfd00000, 0x00000007bfd00000)
  object space 7168K, 5% used [0x00000007bf600000,0x00000007bf65ebc0,0x00000007bfd00000)
 Metaspace       used 2973K, capacity 4556K, committed 4864K, reserved 1056768K
  class space    used 316K, capacity 392K, committed 512K, reserved 1048576K
Disconnected from the target VM, address: '127.0.0.1:62768', transport: 'socket'

Process finished with exit code 1

3、弱引用(Weak Reference)


/**
 * 弱引用
 */
public class WeakReferenceTest {


    public static void main(String[] args) {
        // 创建软引用
        User user = new User(1, "Raven");
        WeakReference<User> weakReference = new WeakReference<User>(user);
        // 取消强引用
        user = null;

        // 从弱引用中获取引用对象
        System.out.println(weakReference.get());
    }

    public static class User {

        public int id;
        public String name;

        public User(int id, String name) {
            this.id = id;
            this.name = name;
        }

        @Override
        public String toString() {
            return "[ id = " + id + " name = " + name + " ]";
        }
    }


}
[ id = 1 name = Raven ]
Heap
 PSYoungGen      total 2560K, used 1839K [0x00000007bfd00000, 0x00000007c0000000, 0x00000007c0000000)
  eden space 2048K, 89% used [0x00000007bfd00000,0x00000007bfecbd88,0x00000007bff00000)
  from space 512K, 0% used [0x00000007bff80000,0x00000007bff80000,0x00000007c0000000)
  to   space 512K, 0% used [0x00000007bff00000,0x00000007bff00000,0x00000007bff80000)
 ParOldGen       total 7168K, used 0K [0x00000007bf600000, 0x00000007bfd00000, 0x00000007bfd00000)
  object space 7168K, 0% used [0x00000007bf600000,0x00000007bf600000,0x00000007bfd00000)
 Metaspace       used 2949K, capacity 4556K, committed 4864K, reserved 1056768K
  class space    used 313K, capacity 392K, committed 512K, reserved 1048576K
    public static void main(String[] args) {
        // 创建软引用
        User user = new User(1, "Raven");
        WeakReference<User> weakReference = new WeakReference<User>(user);
        // 取消强引用
        user = null;

        // 从弱引用中获取引用对象
        System.out.println(weakReference.get());
        // 手动触发 GC
        System.gc();
        System.out.println("GC 之后获取弱引用对象:");
        System.out.println(weakReference.get());
    }

[ id = 1 name = Raven ]
[GC (System.gc()) [PSYoungGen: 1798K->503K(2560K)] 1798K->539K(9728K), 0.0026733 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (System.gc()) [PSYoungGen: 503K->0K(2560K)] [ParOldGen: 36K->444K(7168K)] 539K->444K(9728K), [Metaspace: 2941K->2941K(1056768K)], 0.0166637 secs] [Times: user=0.01 sys=0.00, real=0.02 secs] 
GC 之后获取弱引用对象:
null
Heap
 PSYoungGen      total 2560K, used 101K [0x00000007bfd00000, 0x00000007c0000000, 0x00000007c0000000)
  eden space 2048K, 4% used [0x00000007bfd00000,0x00000007bfd19728,0x00000007bff00000)
  from space 512K, 0% used [0x00000007bff00000,0x00000007bff00000,0x00000007bff80000)
  to   space 512K, 0% used [0x00000007bff80000,0x00000007bff80000,0x00000007c0000000)
 ParOldGen       total 7168K, used 444K [0x00000007bf600000, 0x00000007bfd00000, 0x00000007bfd00000)
  object space 7168K, 6% used [0x00000007bf600000,0x00000007bf66f318,0x00000007bfd00000)
 Metaspace       used 2948K, capacity 4556K, committed 4864K, reserved 1056768K
  class space    used 313K, capacity 392K, committed 512K, reserved 1048576K

4、虚引用(Phantom Reference)


/**
 * 虚引用
 */
public class PhantomReferenceTest {

    public static PhantomReferenceTest obj;
    static ReferenceQueue<PhantomReferenceTest> phantomQueue = null;

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("调用" + PhantomReferenceTest.class + "类的 finalize方法");
    }

    public static void main(String[] args) {
        phantomQueue = new ReferenceQueue<>();
        obj = new PhantomReferenceTest();
        // 构造 PhantomReferenceTest 对象的虚引用,并指定了引用队列
        PhantomReference<PhantomReferenceTest> phantomReference = new PhantomReference<>(obj, phantomQueue);

        try {
            //  获取虚引用对象
            System.out.println("虚引用对象 = " + phantomReference.get());
        }
        catch (Exception e) {
            e.printStackTrace();
        }

    }
}
虚引用对象 = null

    public static void main(String[] args) {
        phantomQueue = new ReferenceQueue<>();
        obj = new PhantomReferenceTest();
        // 构造 PhantomReferenceTest 对象的虚引用,并指定了引用队列
        PhantomReference<PhantomReferenceTest> phantomReference = new PhantomReference<>(obj, phantomQueue);

        try {
            //  获取虚引用对象
            System.out.println("虚引用对象 = " + phantomReference.get());

            // 将强引用设置null
            obj  = null;

            // 第1次手动GC
            System.gc();

            Thread.sleep(1000);
            if (null == obj) {
                System.out.println("obj 是 null");
            }
            else {
                System.out.println("obj 可用");
            }
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }
虚引用对象 = null
调用class com.lkty.reference.PhantomReferenceTest类的 finalize方法
obj 是 null

    public static void main(String[] args) {
        phantomQueue = new ReferenceQueue<>();
        obj = new PhantomReferenceTest();
        // 构造 PhantomReferenceTest 对象的虚引用,并指定了引用队列
        PhantomReference<PhantomReferenceTest> phantomReference = new PhantomReference<>(obj, phantomQueue);

        try {
            //  获取虚引用对象
            System.out.println("虚引用对象 = " + phantomReference.get());

            // 将强引用设置null
            obj  = null;

            // 第1次手动GC
            System.gc();

            Thread.sleep(1000);
            if (null == obj) {
                System.out.println("obj 是 null");
            }
            else {
                System.out.println("obj 可用");
            }

            // 第2次手动GC
            System.out.println("第2次手动GC");
            obj  = null;
            System.gc();
            Thread.sleep(1000);
            if (null == obj) {
                System.out.println("obj 是 null");
            }
            else {
                System.out.println("obj 可用");
            }

        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }
    
虚引用对象 = null
调用class com.lkty.reference.PhantomReferenceTest类的 finalize方法
obj 是 null
第2次手动GC
obj 是 null
package com.lkty.reference;

import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;

/**
 * 虚引用
 */
public class PhantomReferenceTest {

    public static PhantomReferenceTest obj;
    static ReferenceQueue<PhantomReferenceTest> phantomQueue = null;


    /**
     * 线程
     */
    public static class CheckRefQueue extends Thread {
        @Override
        public void run() {
            while (true) {
                if (null != phantomQueue) {
                    PhantomReference<PhantomReferenceTest> objt = null;
                    try {
                        objt = (PhantomReference<PhantomReferenceTest>) phantomQueue.remove();
                    }
                    catch (Exception e) {
                        e.printStackTrace();
                    }
                    if (null != objt) {
                        System.out.println("追踪垃圾回收过程: PhantomReferenceTest被GC了");
                    }
                }
            }
        }
    }

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("调用" + PhantomReferenceTest.class + "类的 finalize方法");
    }

    public static void main(String[] args) {

        CheckRefQueue refQueue = new CheckRefQueue();
        // 设置为守护线程
        refQueue.setDaemon(true);
        refQueue.start();

        phantomQueue = new ReferenceQueue<>();
        obj = new PhantomReferenceTest();
        // 构造 PhantomReferenceTest 对象的虚引用,并指定了引用队列
        PhantomReference<PhantomReferenceTest> phantomReference = new PhantomReference<>(obj, phantomQueue);

        try {
            //  获取虚引用对象
            System.out.println("虚引用对象 = " + phantomReference.get());

            // 将强引用设置null
            obj = null;

            // 第1次手动GC
            System.gc();

            Thread.sleep(1000);
            if (null == obj) {
                System.out.println("obj 是 null");
            } else {
                System.out.println("obj 可用");
            }

            // 第2次手动GC
            System.out.println("第2次手动GC");
            obj = null;
            System.gc();
            Thread.sleep(1000);
            if (null == obj) {
                System.out.println("obj 是 null");
            } else {
                System.out.println("obj 可用");
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}
Connected to the target VM, address: '127.0.0.1:55408', transport: 'socket'
虚引用对象 = null
调用class com.lkty.reference.PhantomReferenceTest类的 finalize方法
obj 是 null
第2次手动GC
追踪垃圾回收过程: PhantomReferenceTest被GC了
obj 是 null
Disconnected from the target VM, address: '127.0.0.1:55408', transport: 'socket'

5、终结器引用

上一篇下一篇

猜你喜欢

热点阅读