您的位置 首页 java

Java 4 种引用类型(Reference)

GC 的基本思想是考察每个对象的可触及性(可达性),就是从GC Root开始是否可以访问到这个对象。如果可以,则可达,否则就是不可达。在 Java 中,可作为GC Roots的对象包括:

  • JVM栈(栈中的本地变量表)中的引用的对象
  • 方法去中类静态属性引用的对象
  • 方法区中常量引用的对象
  • 本地方法栈中JNI(Native方法)引用的对象

对于可触及性,可以包含以下3种状态。

  • 可触及的:从根节点开始,可以到达这个对象。
  • 可复活的:对象的所有引用都被释放,但是对象有可能在finalize()函数中复活
  • 不可触及的:对象的finalize()方法别调用过,并且没有被复活(finalize()方法只会被调用一次)

Java中提供了4个级别的引用:强引用、软引用、弱引用和虚引用。

强引用

强引用是程序中一般使用的引用类型。强引用的对象是可触及的、不可被回收的。例如:

StringBuffer str = new StringBuffer("Hugege");
StringBuffer str1 = str;
 

上述代码内存示意图如下:

强引用内存示意图

​​​​​​​强引用包含以下特点:

  • 可以直接访问目标对象
  • 所指向的对象在恩和时候都不会被系统回收,JVM宁愿抛出OOM,也不会回收对象
  • 可能导致内存泄漏

软引用

软引用是比强引用弱一点的引用类型。一个对象如果只持有软引用,那么当堆空间不足时,就会被回收(由 SoftReference 实现)。代码如下:

/**
 * 软引用在系统堆内存不足是,被回收
 * 
 * VM Args: -Xmx10M
 * 
 * @author xuefeihu
 *
 */public class SoftRef {
public static class User {
public int id;
public String name;
public User(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "User [id=" + id + ", name=" + name + "]";
}
}
public static void main(String[] args) {
User u = new User(1, "xuefeihu");
SoftReference<User> userSoftRef = new SoftReference<User>(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 * 985 * 7]; // 这里与JDK的版本有关(笔者JDK8),过小不会回收,过大会OOM
System.gc();
System.out.println(userSoftRef.get());
}
}
 

增加上述GC参数,运行结果如下:

User [id=1, name=xuefeihu]
After GC: //内存充足时GC
User [id=1, name=xuefeihu]
null //内存不足是GC,数据已被清除
 

综上,GC未必会回收软引用的对象,但是当内存不足时,软引用对象就会被回收,因此软引用对象不会引起内存溢出。

引用队列

每个软引用都可以附带一个引用队列,当对象的可达性状态发生变化时(由可达变为不可达),软引用对象就会进入引用队列。通过这个队列,可以追踪对象的回收情况。代码如下:

/**
 * 每个软引用都可以附带一个引用队列,当对象的可达性状态发生变化时(由可达变为不可达),
 * 软引用对象就会进入引用队列。通过这个队列,可以追踪对象的回收情况。
 * 
 * VM Args: -Xmx10M
 * 
 * @author xuefeihu
 *
 */public class SoftRefQ {
public static class User {
public int id;
public String name;
public User(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "User [id=" + 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 (Exception e) {
e.printStackTrace();
}
if (obj != null) {
System.out.println("user id = " + obj.uid + " is delete");
}
}
}
}
}
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, "xuefeihu");
softQueue = new ReferenceQueue<User>();
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 * 985 * 7];
System.gc();
System.out.println(userSoftRef.get());
Thread.sleep(1000);
}
}
 

使用上述VM参数,执行结果如下:

User [id=1, name=xuefeihu] 
After GC :
User [id=1, name=xuefeihu] //内存充足时,GC结果
try to create byte array and GC //内存不足时GC
user id = 1 is delete //引用队列探测到对象被删除
null //对象已回收
 

弱引用

弱引用是一种比软引用还弱的一种引用。GC时只要发现,就会对其进行回收。一旦被回收时,就会加入到一个引用队列中(和软引用很像),Java中使用WeakReference实现。demo如下:

/**
 * 弱引用在GC时就会被回收
 * 
 * @author xuefeihu
 *
 */public class WeakRef {
public static class User {
public int id;
public String name;
public User(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "User [id=" + id + ", name=" + name + "]";
}
}
public static void main(String[] args) {
User u = new User(1, "xuefeihu");
WeakReference<User> userWeakRef = new WeakReference<User>(u);
u = null;
System.out.println(userWeakRef.get());
System.gc();
// 不管当前内存空间足够与否,都会回收它的内存
System.out.println("After GC: ");
System.out.println(userWeakRef.get());
}
}
 

上述代码运行结果如下:

User [id=1, name=xuefeihu]
After GC: 
null
 

从结果看来,不论内存状况,每次GC都会清除弱引用的对象。

虚引用(幽灵引用)

虚引用是所有引用中最弱的一个。持有虚引用的对象,和没有引用几乎一样,随时都有可能被回收,因此也叫幽灵引用。当使用虚引用的get()方法获得强引用时,总是失败的。虚引用必须和引用队列一起使用,作用于跟踪垃圾回收过程。

下面示例使用虚引用跟踪一个可复活对象的回收。

/**
 * 使用虚引用跟踪一个可复活对象的回收
 * 
 * 当GC回收对象时,如果发现有虚引用,就会将其放入引用队列
 * 虚引用必须和引用队列一起使用,它的作用在于跟踪垃圾回收过程。
 * 
 * @author xuefeihu
 *
 */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 (Exception 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<TraceCanReliveObj>();
obj = new TraceCanReliveObj();
PhantomReference<TraceCanReliveObj> phantomRef = new PhantomReference<TraceCanReliveObj>(obj, phantomQueue);

obj = null;
System.gc();
Thread.sleep(1000);
if(obj == null) {
System.out.println("obj 是 null");
} else {
System.out.println("obj 可用");
}

System.out.println("第2次GC");
obj = null;
System.gc();
Thread.sleep(1000);
if(obj == null) {
System.out.println("obj 是 null");
} else {
System.out.println("obj 可用");
}

}
}
 

上述代码执行结果如下:

CanReliveObj finalize called // 对象复活
obj 可用
第2次GC //对象无法复活
TraceCanReliveObj is delete by GC //引用队列捕获到对象被回收
obj 是 null
 

参考:《实战Java虚拟机》

文章来源:智云一二三科技

文章标题:Java 4 种引用类型(Reference)

文章地址:https://www.zhihuclub.com/184638.shtml

关于作者: 智云科技

热门文章

网站地图