1 引用的作用
引用是用来指向一块内存区域,程序通过引用来获取对象信息,它关系着对象的生命周期。
2 为何需要多种引用类型?
默认为强引用,只要对象被任意强引用所指向,则对象不会被垃圾收集器清理。
那有啥问题呢?
有时有些对象的原始引用被指向为NULL,我们希望JVM可以清理掉这个无用对象,但此刻该对象可能被其他强引用所包含,那总不能一个个将所有引用指向为NULL。此刻就需要其他引用的支持。比如如果其他对象包含的引用是该对象的弱引用,那么JVM会在清理时忽略它的存在。
3 强引用、弱引用、软引用、虚引用
3.1 强引用
强引用是使用最普遍的引用。如果一个对象具有强引用,那垃圾收器绝不会回收它。当内存空间不足,Java虚拟机宁愿抛出OutOfM moryError
错误,使程序异常终止,也不会靠回收具有强引用 对象来解决内存不足的问题。
应用:基本上new出来的都是强引用。除了下面三个家伙
3.2 软引用
如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。
应用:实现内存敏感的高速缓存。
3.3 虚引用
如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。虚引用主要用来跟踪对象被垃圾回收器回收的活动。虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列 (ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到ReferenceQueue中。
应用:
JDK中直接内存的回收就用到虚引用,JAVA
在申请一块直接内存之后,会在堆内存分配一个对象保存这个堆外内存的引用,这个对象被垃圾收集器管理,一旦这个对象被回收,相应的用户线程会收到通知并对直接内存进行清理工作。
3.4 弱引用
如果这个对象只被弱引用关联,那么这个对象就会被垃圾回收器回收。
应用:有时某些缓存和副本创建后,如果被其他用户使用则留着,如果没有使用,那么发生GC时,则希望可以将该类对象可以被清理。如JDK中的:WeakHashMap和ThreadLocal。
举个栗子:
package jvm;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;
/**
* WeakReference测试
*
* @author chaozai
* @date 2019年3月27日
*
*/
public class WeakReferenceTest {
public static void main(String[] args) {
test01();
test02();
test03();
test04();
}
/**
* 测试1:当对象引用被置为null,GC会将对象清理,清理后通过弱引用返回null
*/
public static void test01() {
print("-------------test01-------------");
Long l = new Long(100l);
WeakReference<Long> weakRef = new WeakReference<Long>(l);
print(weakRef.get());
l = null;
print(weakRef.get());
System.gc();
print(weakRef.get());
print("-------------test01-------------");
}
/**
* 测试2:对象引用被存放在MAP中,当对象引用被置为null,GC不会将对象清理,清理后通过弱引用返回对象,MAP中对象存在
*/
public static void test02() {
print("-------------test02-------------");
Long l = new Long(100l);
WeakReference<Long> weakRef = new WeakReference<Long>(l);
Map<String, Long> map = new HashMap<>();
map.put("0", l);
print(weakRef.get());
l = null;
print(weakRef.get());
System.gc();
print(weakRef.get());
print(map.get("0"));
print("-------------test02-------------");
}
/**
* 测试3:对象弱引用被存放在MAP中,当对象引用被置为null,GC会将对象清理,清理后通过弱引用返回null,Map中弱引用存在,但指向对象为Null
*/
public static void test03() {
print("-------------test03-------------");
Long l = new Long(100l);
WeakReference<Long> weakRef = new WeakReference<Long>(l);
Map<String, WeakReference<Long>> map = new HashMap<>();
map.put("0", weakRef);
print(weakRef.get());
l = null;
print(weakRef.get());
System.gc();
print(weakRef.get());
print(map.get("0").get());
print("-------------test03-------------");
}
/**
* 测试4:对象弱引用被其他对象持有,当对象引用被置为null,GC会将对象清理,清理后通过弱引用返回null,持有对象中弱引用指向对象为Null,而强引用对象依然不为Null
*/
public static void test04() {
print("-------------test04-------------");
Long l0 = new Long(100l);
Long l1 = new Long(100l);
WeakReference<Long> weakRef = new WeakReference<Long>(l0);
WeakObj wObj = new WeakObj();
wObj.setL(l1);
wObj.setWl(weakRef);
l0 = null;
l1 = null;
print(wObj.getL());
print(wObj.getWl().get());
System.gc();
print(wObj.getL());
print(wObj.getWl().get());
print("-------------test04-------------");
}
/**
* 通用打印方法
*
* @param o
*/
private static void print(Object o) {
System.out.println(o);
}
}
/**
* 测试对象:分别包含一个弱引用和一个强引用
* @author chaozai
* @date 2019年3月28日
*
*/
class WeakObj {
WeakReference<Long> wl;
Long l;
public WeakReference<Long> getWl() {
return wl;
}
public void setWl(WeakReference<Long> wl) {
this.wl = wl;
}
public Long getL() {
return l;
}
public void setL(Long l) {
this.l = l;
}
}
运行结果:
-------------test01-------------
100
100
[GC (System.gc()) [PSYoungGen: 1229K->728K(17920K)] 1229K->736K(58880K), 0.0021357 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (System.gc()) [PSYoungGen: 728K->0K(17920K)] [ParOldGen: 8K->584K(40960K)] 736K->584K(58880K), [Metaspace: 2665K->2665K(1056768K)], 0.0098673 secs] [Times: user=0.05 sys=0.00, real=0.01 secs]
null
-------------test01-------------
-------------test02-------------
100
100
[GC (System.gc()) [PSYoungGen: 307K->64K(17920K)] 892K->648K(58880K), 0.0141342 secs] [Times: user=0.05 sys=0.00, real=0.01 secs]
[Full GC (System.gc()) [PSYoungGen: 64K->0K(17920K)] [ParOldGen: 584K->584K(40960K)] 648K->584K(58880K), [Metaspace: 2665K->2665K(1056768K)], 0.0216498 secs] [Times: user=0.05 sys=0.00, real=0.02 secs]
100
100
-------------test02-------------
-------------test03-------------
100
100
[GC (System.gc()) [PSYoungGen: 307K->32K(17920K)] 891K->616K(58880K), 0.0090060 secs] [Times: user=0.05 sys=0.00, real=0.01 secs]
[Full GC (System.gc()) [PSYoungGen: 32K->0K(17920K)] [ParOldGen: 584K->584K(40960K)] 616K->584K(58880K), [Metaspace: 2665K->2665K(1056768K)], 0.0093501 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
null
null
-------------test03-------------
-------------test04-------------
100
100
[GC (System.gc()) [PSYoungGen: 307K->96K(17920K)] 891K->680K(58880K), 0.0007292 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (System.gc()) [PSYoungGen: 96K->0K(17920K)] [ParOldGen: 584K->585K(40960K)] 680K->585K(58880K), [Metaspace: 2668K->2668K(1056768K)], 0.0055624 secs] [Times: user=0.02 sys=0.00, real=0.01 secs]
100
null
-------------test04-------------
Heap
PSYoungGen total 17920K, used 614K [0x00000000ec500000, 0x00000000ed900000, 0x0000000100000000)
eden space 15360K, 4% used [0x00000000ec500000,0x00000000ec599b20,0x00000000ed400000)
from space 2560K, 0% used [0x00000000ed680000,0x00000000ed680000,0x00000000ed900000)
to space 2560K, 0% used [0x00000000ed400000,0x00000000ed400000,0x00000000ed680000)
ParOldGen total 40960K, used 585K [0x00000000c4e00000, 0x00000000c7600000, 0x00000000ec500000)
object space 40960K, 1% used [0x00000000c4e00000,0x00000000c4e92440,0x00000000c7600000)
Metaspace used 2675K, capacity 4490K, committed 4864K, reserved 1056768K
class space used 288K, capacity 386K, committed 512K, reserved 1048576K
4 总结
强引用 > 软引用 > 弱引用 > 虚引用
哈哈哈哈,总体来说就是按需使用。尽量循环使用已有对象(使用对象池),不要长期保存无用对象,缓存中的对象多多关注。
爱家人,爱生活,爱设计,爱编程,拥抱精彩人生!