Appearance
Java 四种类型的引用
根据垃圾收集器对引用的行为,有四种类型的 Java 引用。
- 强引用 - 这是 Java 中的默认引用。当创建一个对象时,强引用将被创建。
- 弱引用 - 弱引用可以通过使用
lang.ref.WeakReference
类来创建。 - 软引用 - 软引用可以通过使用
lang.ref.SoftReference
类来创建。 - 虚引用 - 虚引用可以通过使用
lang.ref.PhantomReference
类来创建。
强引用
我们通常在编写 Java 代码创建对象时使用强引用。具有强引用且在内存中处于活动状态的对象不符合垃圾回收的条件,强引用指向 null
的对象可以被垃圾回收。
java
package study.helloworld.hellojava.ref;
/**
* 强引用 - 这是 Java 中的默认引用。当创建一个对象时,强引用将被创建。
*
* 具有强引用且在内存中处于活动状态的对象不符合垃圾回收的条件,强引用指向 null 的对象可以被垃圾回收。
*/
public class StrongReferenceSample {
public static void main(String[] args) {
Object object = new Object();
System.out.println("object=" + object);
object = null;
System.out.println("object=" + object);
}
}
sh
$ java StrongReferenceSample.java
object=java.lang.Object@130f889
object=null
弱引用
弱引用适合进行垃圾回收。一旦 JVM 检测到带有弱引用的对象,这个对象就会被标记,并且当 JVM 运行垃圾收集器线程时,垃圾就会被收集。弱引用可以通过类 lang.ref.WeakReference
创建。
java
package study.helloworld.hellojava.ref;
import java.lang.ref.WeakReference;
/**
* 弱引用 - 弱引用可以通过使用 lang.ref.WeakReference 类来创建。
*
* 一旦 JVM 检测到带有弱引用的对象,这个对象就会被标记,并且当 JVM 运行垃圾收集器线程时,垃圾就会被收集。
*/
public class WeakReferenceSample {
public static void main(String[] args) {
WeakReference<Object> weakReference = new WeakReference<>(new Object());
System.out.println("object=" + weakReference.get());
System.gc();
System.runFinalization();
System.out.println("object=" + weakReference.get());
}
}
sh
$ java WeakReferenceSample.java
object=java.lang.Object@221af3c0
object=null
软引用
直到 JVM 耗尽内存或 JVM 迫切需要内存时,软引用对象才符合垃圾收集的条件。软引用可以通过类 lang.ref.SoftReference
创建。
java
package study.helloworld.hellojava.ref;
import java.lang.ref.SoftReference;
/**
* 软引用 - 软引用可以通过使用 lang.ref.SoftReference 类来创建。
*
* 直到 JVM 耗尽内存或 JVM 迫切需要内存时,软引用对象才符合垃圾收集的条件。
*/
public class SoftReferenceSample {
public static void main(String[] args) {
SoftReference<SoftObject> softReference = new SoftReference<>(
new SoftObject());
System.out.println("object=" + softReference.get());
@SuppressWarnings("unused")
byte[] data = new byte[1024 * 1024];
System.out.println("object=" + softReference.get());
}
private static class SoftObject {
@SuppressWarnings("unused")
private byte[] data = new byte[1024 * 1024];
}
}
sh
$ java -Xms5M -Xmx5M SoftReferenceSample.java
object=study.helloworld.hellojava.ref.SoftReferenceSample$SoftObject@351d0846
object=null
软引用的应用场景(缓存)
java
package study.helloworld.java.ref.cache;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
public class MyCache<K, V> {
private Map<K, SoftReference<V>> map = new ConcurrentHashMap<>();
private ReferenceQueue<V> referenceQueue = new ReferenceQueue<>();
@SuppressWarnings("unchecked")
public void put(K k, V v) {
MySoftReference reference;
while ((reference = (MyCache<K, V>.MySoftReference) referenceQueue
.poll()) != null) {
map.remove(reference.k);
}
map.put(k, new MySoftReference(k, v, referenceQueue));
}
public V get(K k) {
SoftReference<V> softReference = map.get(k);
if (softReference == null) {
return null;
}
V v = softReference.get();
if (v == null) {
map.remove(k);
return null;
}
return v;
}
public int size() {
return map.size();
}
private class MySoftReference extends SoftReference<V> {
private K k;
public MySoftReference(K k, V referent, ReferenceQueue<? super V> q) {
super(referent, q);
this.k = k;
}
}
public static void main(String[] args) {
MyCache<Integer, Byte[]> myCache = new MyCache<>();
int num = 1;
while (true) {
Byte[] data = new Byte[1024 * 1024];
System.out.println(
String.format("cache put num=%s, data=%s", num, data));
myCache.put(num, data);
num++;
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(String.format("cache size=%s", myCache.size()));
}
}
}
sh
$ java -Xms9M -Xmx9M MyCache.java
cache put num=1, data=[Ljava.lang.Byte;@7f552bd3
cache size=1
cache put num=2, data=[Ljava.lang.Byte;@460d0a57
cache size=1
cache put num=3, data=[Ljava.lang.Byte;@47d90b9e
cache size=1
cache put num=4, data=[Ljava.lang.Byte;@1184ab05
cache size=1
cache put num=5, data=[Ljava.lang.Byte;@3aefe5e5
cache size=1
cache put num=6, data=[Ljava.lang.Byte;@149e0f5d
...
虚引用
虚引用的对象可用于垃圾收集,但在垃圾收集之前,一个对象会被放在一个引用队列中。虚引用可以通过类 lang.ref.PhantomReference
创建。
java
package study.helloworld.hellojava.ref;
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
/**
* 虚引用 - 虚引用可以通过使用 lang.ref.PhantomReference 类来创建。
*
* 虚引用的对象可用于垃圾收集,但在垃圾收集之前,一个对象会被放在一个引用队列中。
*/
public class PhantomReferenceSample {
public static void main(String[] args) {
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<Object>();
PhantomReference<Object> phantomReference = new PhantomReference<>(
new Object(), referenceQueue);
System.out.println("phantomReference=" + phantomReference);
System.out.println("object=" + phantomReference.get());
System.out.println("reference=" + referenceQueue.poll());
System.gc();
System.runFinalization();
System.out.println("phantomReference=" + phantomReference);
System.out.println("object=" + phantomReference.get());
System.out.println("reference=" + referenceQueue.poll());
}
}
sh
$ java PhantomReferenceSample.java
phantomReference=java.lang.ref.PhantomReference@29ba4338
object=null
reference=null
phantomReference=java.lang.ref.PhantomReference@29ba4338
object=null
reference=java.lang.ref.PhantomReference@29ba4338