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