强引用
Object o = new Object();这就是强引用,只要某个对象有强引用与之关联,这个对象永远不会被回收,即使内存不足,JVM宁愿抛出OOM,也不回去回收,只有当强引用和对象之间的关联切断了,就可以被回收了,回收特别简单,只需要将o = null;
public class Student {
//当垃圾收集器认为没有指向对象实例的引用时,会在销毁该对象之前调用finalize()方法。
@Override
protected void finalize() throws Throwable {
System.out.println("Student被回收了");
}
public static void main(String[] args) {
Student student = new Student();
student=null;
System.gc();
}
}
在实际的开发中,看到有一些对象被手动赋值为NULL,很大可能就是为了“特意提醒”JVM这块资源可以进行垃圾回收了。
软引用
软引用就是把对象用SoftReference包裹一下,当我们需要从软引用对象获得包裹的对象,只要get一下。。当内存不足,会触发JVM的GC,如果GC后,内存还是不足,就会把软引用的包裹的对象给干掉,也就是只有在内存不足,JVM才会回收该对象。
public class SoftRefeTest {
public static void main(String[] args) {
//在堆内存中开辟10M的空间
SoftReference<byte[]> sf = new SoftReference<byte[]>(new byte[1024 * 1024 * 10]);
System.out.println(sf.get());
System.gc();
System.out.println(sf.get());
//在堆内存中再开辟10M的空间
byte[] bytes = new byte[1024 * 1024 * 10];
System.out.println(sf.get());
}
}
设置最大堆内存20M。
第一次GC,只占用了10M,空间很足,不会回收,第二次GC,空间不够了,jvm回收软引用指向的对象
每一个软引用都可以附带一个引用队列,当对象的可达性状态发生改变时(由可达变为不可达),软引用对象就会进入引用队列。通过这个引用队列,可以跟踪对象的回收情况
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
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 + '\'' +
'}';
}
}
public static class UserSoftReference extends SoftReference<User>{
int uid;
public UserSoftReference(User referent, ReferenceQueue<? super User> q) {
super(referent, q);
uid=referent.id;
}
}
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 delete");
}
}
}
}
}
public static void main(String[] args) throws InterruptedException{
Thread t = new CheckRefQueue();
t.setDaemon(true);
t.start();
User user = new User(1, "geym");
softQueue = new ReferenceQueue<User>();
UserSoftReference userSoftReference = new UserSoftReference(user, softQueue);
user=null;
System.out.println(userSoftReference.get());
System.gc();
System.out.println("After GC");
System.out.println(userSoftReference.get());
System.out.println("try to create byte array and Gc");
byte[] b = new byte[1024 * 965 * 7];
System.gc();
System.out.println(userSoftReference.get());
Thread.sleep(2000);
}
}
弱引用
在系统进行GC时,只要发现弱引用,不管系统堆空间情况如何,都会将对象进行回收,但是,由于垃圾回收器的线程通常优先级很低,因此,并不一定能很快发现持有弱引用的对象,在这种情况下,弱引用对象可以存在较长的时间。一旦一个弱引用对象被来垃圾回收器回收,便会加入到一个注册的引用队列中
public class WeakRef {
public static void main(String[] args) {
SoftRefQ.User user = new SoftRefQ.User(1, "xxx");
WeakReference<SoftRefQ.User> weakReference = new WeakReference<SoftRefQ.User>(user);
user=null;
System.out.println(weakReference.get());
System.gc();
System.out.println("After GC");
System.out.println(weakReference.get());
}
}
虚引用
一个持有虚引用的对象,和没有引用几乎是一样的,随时都可能被卡机回收器回收。当试图通过虚引用的get方法取得强引用时,总是失败。虚引用必须和引用队列一起使用,它的作用是跟踪来记回收过程。
import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
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");
}
}
}
}
}
// 只有在第一次GC的时候会回调
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("CanReliveObj finalize called");
obj = this;
}
@Override
public String toString() {
return "TraceCanReliveObj{}";
}
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> phantomReference = new PhantomReference<TraceCanReliveObj>(obj,phantomQueue);
obj = null;
System.gc();
Thread.sleep(1000);
if (obj ==null) {
System.out.println("obj is null");
}else {
System.out.println("obj可用");
}
System.out.println("第二次gc");
obj = null;
System.gc();
Thread.sleep(1000);
if (obj ==null) {
System.out.println("obj is null");
}else {
System.out.println("obj可用");
}
}
}
第一次可用是在finalize中对象复活的,finalize函数只会被调用一次