java中提供了四个级别的引用:强引用、软引用、弱引用和虚引用。
除强引用外,其它三种均可以在java.lang.ref包中找到它们的身影。
强引用就是程序中一般使用的引用类型,强引用对象是可触及的,不会被回收。
相对的,软引用、弱引用和虚引用的对象是软可触及、弱可触及和虚可触及的,在一定条件下,都可以被回收。
强引用
/**
* Created by wb-xxd249566 on 2017/4/5.
*/
public class StrongReference {
public static void main(String[] args){
StringBuffer str = new StringBuffer("Hello world");
}
}
此段代码运行时,str将被分配在java栈上,而对象StringBuffer实例则被分配在堆上。
局部变量str指向StringBuffer实例所在堆空间,通过str可操作该实例,那么str就是StringBuffer实例的强引用。
此时如果再加一条赋值语句:
/**
* Created by wb-xxd249566 on 2017/4/5.
*/
public class StrongReference {
public static void main(String[] args){
StringBuffer str = new StringBuffer("Hello world");
StringBuffer str1 = str;
}
}
这样str所指向的引用也会被str1所指向,同时在局部变量表上会分配空间存放局部变量str1,此时,该StringBuffer实例就有俩个引用了。
对引用的"=="操作用于表示俩操作数所指向的堆空间的地址是否相同,不表示俩操作数所指向的对象是否相等。
强引用所具备的特点:
1.强引用可以直接访问目标对象
2.强引用所指向的对象在任何时候都不会被系统回收,jvm宁愿抛出OOM异常,也不会回收强引用所指向的对象
3.强引用可能导致内存泄漏
软引用
import java.lang.ref.SoftReference;
/**
* Created by wb-xxd249566 on 2017/4/5.
* -Xmx10m
*/
public class SoftRef {
public static class User{
public User(int id,String name){
this.id = id;
this.name = name;
}
public int id;
public String name;
@Override
public String toString() {
return "[id="+String.valueOf(id)+",name="+name+"]";
}
}
public static void main(String[] args){
User u = new User(1,"xxd");
SoftReference<User> userSoftRef = new SoftReference<>(u);
u = null;
System.out.println(userSoftRef.get());
System.gc();
System.out.println("After GC");
System.out.println(userSoftRef.get());
byte[] b = new byte[1024*925*7];
System.gc();
System.out.println(userSoftRef.get());
}
}
讲道理,由强引用u创建的软引用userSoftRef,在分配了byte[]数组后,内存紧张,应该被GC清除掉,但是在我的计算机上并没有把它清除
由此可见,在我的计算机上,相同的配置,在分配了1024*925*7这么大空间后,并没有使系统感觉资源紧张,因此没有回收该软引用,修改参数,最终在分配了1024*937*7的空间后
byte[] b = new byte[1024*937*7];
执行结果:
所以从该示例中可以看出,GC 未必会回收软引用的对象,但是当内存资源紧张时,软引用对象会被回收,所以软引用对象不会引起内存溢出。
每一个软引用都可以附带一个引用队列,当对象的可达性状态发生变化时(由可达变为不可达),软引用对象就会进入引用队列,可以跟踪对象的回收状况。
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
/**
* Created by wb-xxd249566 on 2017/4/5.
*/
public class SoftRefQ {
public static class User{
public User(int id,String name){
this.id = id;
this.name = name;
}
public int id;
public String name;
@Override
public String toString() {
return "[id="+String.valueOf(id)+",name="+name+"]";
}
}
static ReferenceQueue<User> softQueue = null;
public static class CheckRefQueue extends Thread{
@Override
public void run() {
while (true){
if (softQueue != null){
UserSoftReference obj = null;
try {
obj = (UserSoftReference) softQueue.remove();
}catch (InterruptedException e){
e.printStackTrace();
}
if (obj!=null)
System.out.println("user id "+obj.uid+" is deleted");
}
}
}
}
public static class UserSoftReference extends SoftReference<User>{
int uid;
public UserSoftReference(User referent, ReferenceQueue<? super User> q){
super(referent,q);
uid = referent.id;
}
}
public static void main(String[] args) throws InterruptedException{
Thread t = new CheckRefQueue();
t.setDaemon(true);
t.start();
User u = new User(1,"xxd");
softQueue = new ReferenceQueue<>();
UserSoftReference userSoftRef = new UserSoftReference(u,softQueue);
u = null;
System.out.println(userSoftRef.get());
System.gc();
System.out.println("After GC");
System.out.println(userSoftRef.get());
System.out.println("try to create byte array and GC");
byte[] b = new byte[1024*937*7];
System.gc();
System.out.println(userSoftRef.get());
Thread.sleep(1000);
}
}
执行结果(-Xmx10m):
弱引用
弱引用是一种比软引用较弱的引用类型。在系统GC时,只要发现弱引用,不管系统堆空间使用情况如何,都会将对象进行回收。
一旦一个弱引用对象被垃圾回收器回收,便会加入到一个注册的引用队列中(与软引用类似)
import java.lang.ref.WeakReference;
/**
* Created by wb-xxd249566 on 2017/4/5.
*/
public class WeakRef {
public static class User{
public User(int id,String name){
this.id = id;
this.name = name;
}
public int id;
public String name;
@Override
public String toString() {
return "[id="+String.valueOf(id)+",name="+name+"]";
}
}
public static void main(String[] args){
User u = new User(1,"xxd");
WeakReference<User> userWeakReference = new WeakReference<>(u);
u = null;
System.out.println(userWeakReference.get());
System.gc();
System.out.println("After GC");
System.out.println(userWeakReference.get());
}
}
执行结果:
软引用、弱引用都非常适合来保存那些可有可无的缓存数据。如果这么做,当系统内存不足时,这些缓存数据会被回收,不会导致内存溢出。而当内存资源充足时,这些缓存数据又可以存在相当长的时间,从而起到加速系统的作用。
虚引用
虚引用是所有引用当中类型最弱的一个。一个持有虚引用的对象,和没有引用几乎是一模一样的,随时都可能被垃圾回收器回收。
当视图通过虚引用的get()方法取得强引用时,总是会失败。并且,虚引用必须和引用队列一起使用,它的作用在于跟踪垃圾回收过程。
当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象后,将这个虚引用加入引用队列,以通知应用程序对象的回收情况。
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
/**
* Created by wb-xxd249566 on 2017/4/5.
*/
public class TraceCanReliveObj {
public static TraceCanReliveObj obj;
static ReferenceQueue<TraceCanReliveObj> phantomQueue = null;
public static class CheckRefQueue extends Thread{
@Override
public void run() {
while (true){
if (phantomQueue != null){
PhantomReference<TraceCanReliveObj> objt = null;
try {
objt = (PhantomReference<TraceCanReliveObj>) phantomQueue.remove();
}catch (InterruptedException e){
e.printStackTrace();
}
if(objt!=null){
System.out.println("TraceCanReliveObj is delete by GC");
}
}
}
}
}
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("CanReliveObj finalize called");
obj =this;
}
@Override
public String toString() {
return "I am CanReliveObj";
}
public static void main(String[] args) throws InterruptedException{
Thread t = new CheckRefQueue();
t.setDaemon(true);
t.start();
phantomQueue = new ReferenceQueue<>();
obj = new TraceCanReliveObj();
PhantomReference<TraceCanReliveObj> phantomReference = new PhantomReference<>(obj,phantomQueue);
obj = null;
System.gc();
Thread.sleep(1000);
if (obj == null){
System.out.println("obj is null");
}else{
System.out.println("obj is useful");
}
System.out.println("The second GC");
obj = null;
System.gc();
Thread.sleep(1000);
if (obj == null) {
System.out.println("obj is null");
}else{
System.out.println("obj is useful");
}
}
}
由于虚引用可以跟踪对象的回收时间,因此,可以将一些资源释放操作放置在虚引用中执行和记录。