JAVA四种引用类型
每种编程语言都有自己操作内存中元素的方式,例如在 C 和 C++ 里是通过指针,而在 Java 中则是通过“引用”。
在 Java 中一切都被视为了对象,但是我们操作的标识符实际上是对象的一个引用(reference)。
//创建一个引用,引用可以独立存在,并不一定需要与一个对象关联
String s;
通过将这个叫“引用”的标识符指向某个对象,之后便可以通过这个引用来实现操作对象了。
String s = "hello world";
System.out.println(s);
Java 中的垃圾回收机制在判断是否回收某个对象的时候,都需要依据“引用”这个概念。
在 JDK.1.2 之后,Java 对引用的概念进行了扩充,将引用分为了:强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)、虚引用(Phantom Reference)4 种,这 4 种引用的强度依次减弱。
强引用(Strong Reference)
把一个对象赋给一个引用变量,这个引用变量就是一个强引用。
Java中默认声明的就是强引用,比如:
//只要strongReference 还指向Object对象,Object对象就不会被回收
Object strongReference = new Object();
当一个对象被强引用变量引用时,它处于可达状态,它是不可能被垃圾回收机制回收的,即使内存不足时,JVM也会直接抛出 OutOfMemoryError,不会去回收。
因此强引用是造成 Java 内存泄漏的主要原因之一:
配置参数 -Xms2M -Xmx3M,将 JVM 的初始内存设为2M,最大可用内存为 3M,然后连续创建了 10 个大小为 1M 的字节数组,循环遍历将这些对象打印出来:
/*强引用*/
List<Object> list = new ArrayList<>();
//创建10个1M的强引用字节数组
for(int i = 0; i < 10; i++) {
byte[] buff = new byte[1024 * 1024];
list.add(buff);
}
//主动通知垃圾回收
System.gc();
for(Object obj : list) {
System.out.println(obj);
}
输出结果:
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
如果想中断强引用与对象之间的联系,可以显示的将强引用赋值为null,这样一来,JVM就可以适时的回收对象了:
strong = null;//显示的将强引用赋值为null
软引用(Soft Reference)
软引用需要用 SoftReference 类来实现。
软引用是用来描述一些非必需但仍有用的对象。对于只有软引用的对象来说,当系统内存足够时它不会被回收,当系统内存空间不足时它会被回收。
配置参数 -Xms2M -Xmx3M,将 JVM 的初始内存设为2M,最大可用内存为 3M,然后连续创建了 10 个大小为 1M 的字节数组,并赋值给了软引用,循环遍历将这些对象打印出来:
List<SoftReference> list = new ArrayList<>();
//创建10个1M的软引用字节数组
for(int i = 0; i < 10; i++) {
byte[] buff = new byte[1024 * 1024];
list.add(new SoftReference<>(buff));
}
//遍历输出软引用的对象(注意要使用SoftReference的get方法)
for(SoftReference softReference : list) {
System.out.println(softReference.get());
}
输出结果:
null
null
null
null
null
null
null
null
null
[B@4554617c
无论循环创建多少个软引用对象,打印结果总是只有最后一个对象被保留,其他的softReference全都被置空回收了。
这就说明了在内存不足的情况下,软引用将会被自动回收。
软引用通常用在对内存敏感的程序中,一般被用来实现缓存技术,比如网页缓存,图片缓存等。
弱引用(Weak Reference)
弱引用需要用 WeakReference 类来实现。
它比软引用的生存期更短,对于只有弱引用的对象来说,只要垃圾回收机制一运行,不管 JVM 的内存空间是否足够,总会回收该对象占用的内存。
连续创建 5 个字节数组,并赋值给了弱引用,循环遍历将这些对象打印出来:
/*弱引用*/
List<WeakReference> list = new ArrayList<>();
//创建5个弱引用字节数组
for(int i = 0; i < 5; i++) {
byte[] buff = new byte[1];
list.add(new WeakReference<>(buff));
}
System.out.println("回收前:");
for(WeakReference WeakReference : list) {
System.out.println(WeakReference.get());
}
//主动通知垃圾回收
System.gc();
System.out.println("回收后:");
for(WeakReference WeakReference : list) {
System.out.println(WeakReference.get());
}
输出结果:
回收前:
[B@4554617c
[B@74a14482
[B@1540e19d
[B@677327b6
[B@14ae5a5
回收后:
null
null
null
null
null
虚引用(Phantom Reference)
虚引用需要 PhantomReference 类来实现,它不能单独使用,必须和引用队列联合使用。
虚引用的主要作用是跟踪对象被垃圾回收的状态。
详见你不可不知的Java引用类型之——虚引用。