美文网首页
谈谈JVM中的引用 - 强引用,弱引用,软引用,虚引用

谈谈JVM中的引用 - 强引用,弱引用,软引用,虚引用

作者: overflowedstack | 来源:发表于2021-08-05 16:36 被阅读0次
    1. 强引用
    什么是强引用

    当某个对象与强引用关联,哪怕当垃圾回收内存不够,该对象都不会被回收。JVM会抛出out of memory异常。

    强引用示例

    先一共分配了10MB内存,接下来再分配7MB时,内存不够,直接抛出OOM异常。

    public class ReferenceTest {
        
        private static int _1MB = 1024*1024;
        
        //-Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8
        public static void main(String[] args) {
            ReferenceTest rt = new ReferenceTest();
            rt.strongReference();
        }
    
        public void strongReference() {
            byte[] a = new byte[2*_1MB];
            byte[] b = new byte[3*_1MB];
            byte[] c = new byte[5*_1MB];
            System.out.println("10MB is allocated");
            
            byte[] d = new byte[7*_1MB];
        }
    }
    

    输出:

    10MB is allocated
    [GC (Allocation Failure) --[PSYoungGen: 6143K->6143K(9216K)] 11263K->13311K(19456K), 0.0031110 secs] [Times: user=0.00 sys=0.01, real=0.00 secs] 
    [Full GC (Ergonomics) [PSYoungGen: 6143K->3444K(9216K)] [ParOldGen: 7168K->7168K(10240K)] 13311K->10612K(19456K), [Metaspace: 2684K->2684K(1056768K)], 0.0051359 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
    [GC (Allocation Failure) --[PSYoungGen: 3444K->3444K(9216K)] 10612K->10612K(19456K), 0.0013374 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
    [Full GC (Allocation Failure) [PSYoungGen: 3444K->3432K(9216K)] [ParOldGen: 7168K->7168K(10240K)] 10612K->10600K(19456K), [Metaspace: 2684K->2684K(1056768K)], 0.0070760 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
    Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
        at com.example.demo.jvm.ReferenceTest.strongReference(ReferenceTest.java:21)
        at com.example.demo.jvm.ReferenceTest.main(ReferenceTest.java:12)
    
    2. 软引用
    什么是软引用

    当某个对象与软引用关联,仅当垃圾回收内存不够时,该对象才会被回收。

    软引用示例1

    先创建一个软引用对象,8MB。
    再创建一个强引用对象,8MB。分配内存时,发现内存不够,触发GC,回收了软引用对象。成功分配内存给此强引用对象。
    接下来创建一个强引用对象,8MB。内存不够,触发GC,内存依然不够。

    public class ReferenceTest {
        
        private static int _1MB = 1024*1024;
        
        //-Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8
        public static void main(String[] args) {
            ReferenceTest rt = new ReferenceTest();
            rt.softReference();
        }
        
        public void softReference() {
            SoftReference<Object> ar = new SoftReference<Object>(new byte[8*_1MB]);
            System.out.println("ar address: " + ar);
            System.out.println("ar.get() address: " + ar.get());
    
            byte[] b = new byte[8*_1MB];
            System.out.println("ar.get() address: " + ar.get());
            System.out.println("b address: " + b);
            
            byte[] c = new byte[8*_1MB];
            System.out.println("c address: " + c);
        }
    }
    
    ar address: java.lang.ref.SoftReference@33909752
    ar.get() address: [B@55f96302
    
    [GC (Allocation Failure) [PSYoungGen: 1023K->480K(9216K)] 9215K->8680K(19456K), 0.0010300 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
    [GC (Allocation Failure) [PSYoungGen: 480K->416K(9216K)] 8680K->8616K(19456K), 0.0083859 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
    [Full GC (Allocation Failure) [PSYoungGen: 416K->0K(9216K)] [ParOldGen: 8200K->8565K(10240K)] 8616K->8565K(19456K), [Metaspace: 2686K->2686K(1056768K)], 0.0073300 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
    [GC (Allocation Failure) [PSYoungGen: 0K->0K(9216K)] 8565K->8565K(19456K), 0.0014722 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
    [Full GC (Allocation Failure) [PSYoungGen: 0K->0K(9216K)] [ParOldGen: 8565K->361K(10240K)] 8565K->361K(19456K), [Metaspace: 2686K->2686K(1056768K)], 0.0126119 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
    
    ar.get() address: null
    b address: [B@3d4eac69
    
    [GC (Allocation Failure) [PSYoungGen: 163K->32K(9216K)] 8717K->8585K(19456K), 0.0004216 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
    [GC (Allocation Failure) [PSYoungGen: 32K->32K(9216K)] 8585K->8585K(19456K), 0.0002950 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
    [Full GC (Allocation Failure) [PSYoungGen: 32K->0K(9216K)] [ParOldGen: 8553K->8550K(10240K)] 8585K->8550K(19456K), [Metaspace: 2687K->2687K(1056768K)], 0.0126541 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
    [GC (Allocation Failure) [PSYoungGen: 0K->0K(9216K)] 8550K->8550K(19456K), 0.0033840 secs] [Times: user=0.00 sys=0.01, real=0.00 secs] 
    [Full GC (Allocation Failure) [PSYoungGen: 0K->0K(9216K)] [ParOldGen: 8550K->8550K(10240K)] 8550K->8550K(19456K), [Metaspace: 2687K->2687K(1056768K)], 0.0036786 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
    Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
        at com.example.demo.jvm.ReferenceTest.softReference(ReferenceTest.java:37)
        at com.example.demo.jvm.ReferenceTest.main(ReferenceTest.java:12)
    Heap
     PSYoungGen      total 9216K, used 491K [0x00000007bf600000, 0x00000007c0000000, 0x00000007c0000000)
      eden space 8192K, 6% used [0x00000007bf600000,0x00000007bf67afb0,0x00000007bfe00000)
      from space 1024K, 0% used [0x00000007bff00000,0x00000007bff00000,0x00000007c0000000)
      to   space 1024K, 0% used [0x00000007bfe00000,0x00000007bfe00000,0x00000007bff00000)
     ParOldGen       total 10240K, used 8550K [0x00000007bec00000, 0x00000007bf600000, 0x00000007bf600000)
      object space 10240K, 83% used [0x00000007bec00000,0x00000007bf4599f8,0x00000007bf600000)
     Metaspace       used 2718K, capacity 4486K, committed 4864K, reserved 1056768K
      class space    used 291K, capacity 386K, committed 512K, reserved 1048576K
    
    软引用示例2 - 引用队列

    创建一个软引用,指向的对象8MB。此时引用队列为空。
    再创建一个软引用,指向对象8MB。此时内存不够,触发垃圾回收。第一个软引用对象被回收,此时查看引用队列,可以poll到第一个引用对象。

    public class ReferenceTest {
        
        private static int _1MB = 1024*1024;
        
        //-Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8
        public static void main(String[] args) {
            ReferenceTest rt = new ReferenceTest();
            rt.softReferenceWithQueue();
        }
    
        public void softReferenceWithQueue() {
            ReferenceQueue queue = new ReferenceQueue (); 
            SoftReference<Object> ar = new SoftReference<Object>(new byte[8*_1MB], queue);
            System.out.println("ar address: " + ar);
            System.out.println("ar.get() address: " + ar.get());
            System.out.println("Poll from queue: " + queue.poll());
            
            SoftReference<Object> br = new SoftReference<Object>(new byte[8*_1MB], queue);
            System.out.println("br address: " + br);
            System.out.println("br.get() address: " + br.get());
            System.out.println("Poll from queue: " + queue.poll());
        }
    }
    
    ar address: java.lang.ref.SoftReference@33909752
    ar.get() address: [B@55f96302
    Poll from queue: null
    
    [GC (Allocation Failure) [PSYoungGen: 1023K->448K(9216K)] 9215K->8648K(19456K), 0.0015300 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
    [GC (Allocation Failure) [PSYoungGen: 448K->432K(9216K)] 8648K->8632K(19456K), 0.0007787 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
    [Full GC (Allocation Failure) [PSYoungGen: 432K->0K(9216K)] [ParOldGen: 8200K->8566K(10240K)] 8632K->8566K(19456K), [Metaspace: 2685K->2685K(1056768K)], 0.0040946 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
    [GC (Allocation Failure) [PSYoungGen: 0K->0K(9216K)] 8566K->8566K(19456K), 0.0003710 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
    [Full GC (Allocation Failure) [PSYoungGen: 0K->0K(9216K)] [ParOldGen: 8566K->361K(10240K)] 8566K->361K(19456K), [Metaspace: 2685K->2685K(1056768K)], 0.0037217 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
    
    br address: java.lang.ref.SoftReference@3d4eac69
    br.get() address: [B@42a57993
    Poll from queue: java.lang.ref.SoftReference@33909752
    
    Heap
     PSYoungGen      total 9216K, used 246K [0x00000007bf600000, 0x00000007c0000000, 0x00000007c0000000)
      eden space 8192K, 3% used [0x00000007bf600000,0x00000007bf63d8a8,0x00000007bfe00000)
      from space 1024K, 0% used [0x00000007bfe00000,0x00000007bfe00000,0x00000007bff00000)
      to   space 1024K, 0% used [0x00000007bff00000,0x00000007bff00000,0x00000007c0000000)
     ParOldGen       total 10240K, used 8553K [0x00000007bec00000, 0x00000007bf600000, 0x00000007bf600000)
      object space 10240K, 83% used [0x00000007bec00000,0x00000007bf45a740,0x00000007bf600000)
     Metaspace       used 2692K, capacity 4490K, committed 4864K, reserved 1056768K
      class space    used 288K, capacity 386K, committed 512K, reserved 1048576K
    
    软引用的应用场景

    缓存。在使用缓存时有一个原则,如果缓存中有就从缓存获取,如果没有就从数据库中获取,那么软引用就比较适用于这样的场景。

    3. 弱引用
    什么是弱引用

    当某个对象与弱引用关联,一旦进行垃圾回收,无论内存是否充足,该对象都会被回收。

    弱引用示例1

    先创建了一个弱引用对象,8MB。
    调用垃圾回收。虽然内存空间充足,但此弱引用对象依然被回收。

    public class ReferenceTest {
        
        private static int _1MB = 1024*1024;
        
        //-Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8
        public static void main(String[] args) {
            ReferenceTest rt = new ReferenceTest();
            rt.weakReference();
        }
    
        public void weakReference() {
            WeakReference<Object> ar = new WeakReference<Object>(new byte[8*_1MB]);
            System.out.println("ar address: " + ar);
            System.out.println("ar.get() address: " + ar.get());
            
            System.gc();
            System.out.println("ar address: " + ar);
            System.out.println("ar.get() address: " + ar.get());
        }
    }
    
    ar address: java.lang.ref.WeakReference@33909752
    ar.get() address: [B@55f96302
    
    [GC (System.gc()) [PSYoungGen: 1023K->512K(9216K)] 9215K->8712K(19456K), 0.0014139 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
    [Full GC (System.gc()) [PSYoungGen: 512K->0K(9216K)] [ParOldGen: 8200K->373K(10240K)] 8712K->373K(19456K), [Metaspace: 2684K->2684K(1056768K)], 0.0040597 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
    
    ar address: java.lang.ref.WeakReference@33909752
    ar.get() address: null
    
    Heap
     PSYoungGen      total 9216K, used 246K [0x00000007bf600000, 0x00000007c0000000, 0x00000007c0000000)
      eden space 8192K, 3% used [0x00000007bf600000,0x00000007bf63d8d8,0x00000007bfe00000)
      from space 1024K, 0% used [0x00000007bfe00000,0x00000007bfe00000,0x00000007bff00000)
      to   space 1024K, 0% used [0x00000007bff00000,0x00000007bff00000,0x00000007c0000000)
     ParOldGen       total 10240K, used 373K [0x00000007bec00000, 0x00000007bf600000, 0x00000007bf600000)
      object space 10240K, 3% used [0x00000007bec00000,0x00000007bec5d658,0x00000007bf600000)
     Metaspace       used 2691K, capacity 4486K, committed 4864K, reserved 1056768K
      class space    used 288K, capacity 386K, committed 512K, reserved 1048576K
    
    弱引用示例2 - 引用队列

    创建一个弱引用,指向对象8MB,此时引用队列为空。
    调用垃圾回收,弱引用的对象被回收,此时引用队列里有此弱引用。

    public class ReferenceTest {
        
        private static int _1MB = 1024*1024;
        
        //-Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8
        public static void main(String[] args) {
            ReferenceTest rt = new ReferenceTest();
            rt.weakReferenceWithQueue();
        }
    
        public void weakReferenceWithQueue() {
            ReferenceQueue queue = new ReferenceQueue (); 
            WeakReference<Object> ar = new WeakReference<Object>(new byte[8*_1MB], queue);
            System.out.println("ar address: " + ar);
            System.out.println("ar.get() address: " + ar.get());
            System.out.println("Poll from queue: " + queue.poll());
            
            System.gc();
            System.out.println("ar address: " + ar);
            System.out.println("ar.get() address: " + ar.get());
            System.out.println("Poll from queue: " + queue.poll());
        }
    }
    
    ar address: java.lang.ref.WeakReference@33909752
    ar.get() address: [B@55f96302
    Poll from queue: null
    
    [GC (System.gc()) [PSYoungGen: 1023K->512K(9216K)] 9215K->8712K(19456K), 0.0007464 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
    [Full GC (System.gc()) [PSYoungGen: 512K->0K(9216K)] [ParOldGen: 8200K->374K(10240K)] 8712K->374K(19456K), [Metaspace: 2685K->2685K(1056768K)], 0.0034212 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
    
    ar address: java.lang.ref.WeakReference@33909752
    ar.get() address: null
    Poll from queue: java.lang.ref.WeakReference@33909752
    
    Heap
     PSYoungGen      total 9216K, used 246K [0x00000007bf600000, 0x00000007c0000000, 0x00000007c0000000)
      eden space 8192K, 3% used [0x00000007bf600000,0x00000007bf63d8d8,0x00000007bfe00000)
      from space 1024K, 0% used [0x00000007bfe00000,0x00000007bfe00000,0x00000007bff00000)
      to   space 1024K, 0% used [0x00000007bff00000,0x00000007bff00000,0x00000007c0000000)
     ParOldGen       total 10240K, used 374K [0x00000007bec00000, 0x00000007bf600000, 0x00000007bf600000)
      object space 10240K, 3% used [0x00000007bec00000,0x00000007bec5db68,0x00000007bf600000)
     Metaspace       used 2692K, capacity 4490K, committed 4864K, reserved 1056768K
      class space    used 288K, capacity 386K, committed 512K, reserved 1048576K
    
    弱引用的应用场景

    WeakHashMap,ThreadLocal里都用到了弱引用。

    4. 虚引用
    什么是虚引用

    虚引用,是一个形同虚设的引用,不能通过虚引用获取到它引用的对象。虚引用要和引用队列一起使用,当虚引用所指向的对象被回收后,此虚引用会被加到引用队列里。因此,虚引用主要用于跟踪对象被垃圾回收的情况。

    虚引用示例

    先创建一个虚引用,指向8MB的对象。用get方法得不到引用的对象。另外,可以看到引用队列里还是空的。
    调用垃圾回收,再查看引用队列,发现虚引用本身被加到引用队列里。

    public class ReferenceTest {
        
        private static int _1MB = 1024*1024;
        
        //-Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8
        public static void main(String[] args) {
            ReferenceTest rt = new ReferenceTest();
            rt.phantomReference();
        }
    
        public void phantomReference() {
            ReferenceQueue queue = new ReferenceQueue (); 
            PhantomReference pr = new PhantomReference (new byte[8*_1MB], queue);
            System.out.println("phantom reference:" + pr);
            System.out.println("Get the referenced object: " + pr.get());
            System.out.println("Poll from queue:" + queue.poll());
            
            System.gc();
            try {
                Thread.sleep(1000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            
            System.out.println("Poll from queue:" + queue.poll());
        }
    }
    
    phantom reference:java.lang.ref.PhantomReference@33909752
    Get the referenced object: null
    Poll from queue:null
    
    [GC (System.gc()) [PSYoungGen: 1023K->480K(9216K)] 9215K->8680K(19456K), 0.0007888 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
    [Full GC (System.gc()) [PSYoungGen: 480K->0K(9216K)] [ParOldGen: 8200K->8566K(10240K)] 8680K->8566K(19456K), [Metaspace: 2684K->2684K(1056768K)], 0.0040426 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
    
    Poll from queue:java.lang.ref.PhantomReference@33909752
    
    Heap
     PSYoungGen      total 9216K, used 246K [0x00000007bf600000, 0x00000007c0000000, 0x00000007c0000000)
      eden space 8192K, 3% used [0x00000007bf600000,0x00000007bf63d890,0x00000007bfe00000)
      from space 1024K, 0% used [0x00000007bfe00000,0x00000007bfe00000,0x00000007bff00000)
      to   space 1024K, 0% used [0x00000007bff00000,0x00000007bff00000,0x00000007c0000000)
     ParOldGen       total 10240K, used 8566K [0x00000007bec00000, 0x00000007bf600000, 0x00000007bf600000)
      object space 10240K, 83% used [0x00000007bec00000,0x00000007bf45d9d8,0x00000007bf600000)
     Metaspace       used 2691K, capacity 4486K, committed 4864K, reserved 1056768K
      class space    used 288K, capacity 386K, committed 512K, reserved 1048576K
    
    虚引用的应用场景

    虚引用能帮助你知道,引用的对象何时从内存中被清除。
    那么,可以用于一些场景例如大图片的处理。当确定某一个大图片对象应该被移除,那么可以利用虚引用,确认该对象被移除后,再加载下一个大图片。这样可以尽可能避免内存溢出。

    相关文章

      网友评论

          本文标题:谈谈JVM中的引用 - 强引用,弱引用,软引用,虚引用

          本文链接:https://www.haomeiwen.com/subject/paqnultx.html