想写这个系列很久了,对自己也是个总结与提高。原来在学JAVA时,那些JAVA入门书籍会告诉你一些规律还有法则,但是用的时候我们一般很难想起来,因为我们用的少并且不知道为什么。知其所以然方能印象深刻并学以致用。
本篇文章针对引用分析,是后面分析各种框架机制的基础
强引用就是指在程序代码之中普遍存在的,一般的new一个对象并赋值给一个对象变量,就是一个强引用;只要某个对象有强引用与之关联,JVM必定不会回收这个对象,即使在内存不足的情况下,JVM宁愿抛出OutOfMemory错误也不会回收这种对象。
Object object = new Object();
String str = "hello";
如果想中断强引用和某个对象之间的关联,可以显示地将引用赋值为null,这样一来的话,JVM在合适的时间就会回收该对象。很多JAVA集合类通过这种方式来移除一个对象,例如ArrayList:
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index, numMoved);
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
软引用是用来描述一些有用但并不是必需的对象,在Java中用java.lang.ref.SoftReference类来表示。。对于软引用关联着的对象,在系统将要发生内存溢出异常之前,将会把这些对象列进回收范围之中进行第二次回收。如果这次回收还没有足够的内存,才会抛出内存溢出异常。
对于软引用关联着的对象,如果内存充足,则垃圾回收器不会回收该对象,如果内存不够了,就会回收这些对象的内存。因此,这一点可以很好地用来解决OOM的问题,并且这个特性很适合用来实现缓存:比如网页缓存、图片缓存等。
软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被JVM回收,这个软引用就会被加入到与之关联的引用队列中
public class SoftReferenceDemo {
public static void main(String[] args) {
//初始化,声明强引用
TestNormalObject testNormalObject = new TestNormalObject();
//声明软引用
SoftReference testNormalObjectSoftReference = new SoftReference<>(testNormalObject);
//初始化,声明强引用
TestFinalizeObject testFinalizeObject = new TestFinalizeObject();
//声明软引用
SoftReference testFinalizeObjectSoftReference = new SoftReference<>(testFinalizeObject);
//强引用去掉。让对象只剩下软引用
testNormalObject = null;
testFinalizeObject = null;
//看是否还存在
System.out.println(testNormalObjectSoftReference.get());
System.out.println(testFinalizeObjectSoftReference.get());
try {
//我们启动参数设置的是-Xmx16m,所以这里一定会抛出内存溢出的错误
//也一定会触发回收软引用
byte[] test = new byte[1024 * 1024 * 16];
} catch (Error e) {
System.out.println(e.getClass().getName() + ":" + e.getMessage());
}
//看是否还存在
System.out.println(testNormalObjectSoftReference.get());
System.out.println(testFinalizeObjectSoftReference.get());
}
/**
* 第一种类。未覆盖finalize
*/
public static class TestNormalObject {
}
/**
* 第二种类。覆盖finalize的类
*/
public static class TestFinalizeObject {
@Override
protected void finalize() throws Throwable {
System.out.println("Finalize is called");
}
}
}
输出为:
SoftReferenceDemo$TestNormalObject@31b7dea0
SoftReferenceDemo$TestFinalizeObject@3ac42916
Finalize is called
java.lang.OutOfMemoryError:Java heap space
null
null
看一个软引用是否被垃圾回收一种方式是通过get()方法看返回是否为null判断,或者在构造的时候传入一个队列,之后根据这个队列中有没有这个对象来判断是否被回收了
public class SoftReferenceDemo {
public static void main(String[] args) throws InterruptedException {
ReferenceQueue testNormalObjectReferenceQueue = new ReferenceQueue<>();
ReferenceQueue testFinalizeObjectReferenceQueue = new ReferenceQueue<>();
//初始化,声明强引用
TestNormalObject testNormalObject = new TestNormalObject();
//声明软引用
SoftReference testNormalObjectSoftReference = new SoftReference<>(testNormalObject, testNormalObjectReferenceQueue);
//初始化,声明强引用
TestFinalizeObject testFinalizeObject = new TestFinalizeObject();
//声明软引用
SoftReference testFinalizeObjectSoftReference = new SoftReference<>(testFinalizeObject, testFinalizeObjectReferenceQueue);
System.out.println("Origin: " + testNormalObjectSoftReference);
System.out.println("Origin: " + testFinalizeObjectSoftReference);
Thread thread1 = new Thread() {
@Override
public void run() {
try {
System.out.println("GOT From Queue: " + testNormalObjectReferenceQueue.remove().get());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
thread1.start();
Thread thread2 = new Thread() {
@Override
public void run() {
try {
System.out.println("GOT From Queue: " + testFinalizeObjectReferenceQueue.remove().get());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
thread2.start();
//强引用去掉。让对象只剩下软引用
testNormalObject = null;
testFinalizeObject = null;
//看是否还存在
System.out.println(testNormalObjectSoftReference.get());
System.out.println(testFinalizeObjectSoftReference.get());
try {
//我们启动参数设置的是-Xmx16m,所以这里一定会抛出内存溢出的错误
//也一定会触发回收软引用
byte[] test = new byte[1024 * 1024 * 16];
} catch (Error e) {
System.out.println(e.getClass().getName() + ":" + e.getMessage());
}
//看是否还存在
System.out.println(testNormalObjectSoftReference.get());
System.out.println(testFinalizeObjectSoftReference.get());
thread1.join();
thread2.join();
}
/**
* 第一种类。未覆盖finalize
*/
public static class TestNormalObject {
}
/**
* 第二种类。覆盖finalize的类
*/
public static class TestFinalizeObject {
@Override
protected void finalize() throws Throwable {
System.out.println("Finalize is called");
}
}
}
这时候,输出并不确定,有时候是:
Origin: java.lang.ref.SoftReference@31b7dea0
Origin: java.lang.ref.SoftReference@3ac42916
com.github.hashZhang.scanfold.jdk.reference.SoftReferenceDemo$TestNormalObject@22a71081
com.github.hashZhang.scanfold.jdk.reference.SoftReferenceDemo$TestFinalizeObject@3930015a
java.lang.OutOfMemoryError:Java heap space
null
null
GOT From Queue: null
GOT From Queue: null
Finalize is called
有时候是:
Origin: java.lang.ref.SoftReference@31b7dea0
Origin: java.lang.ref.SoftReference@3ac42916
com.github.hashZhang.scanfold.jdk.reference.SoftReferenceDemo$TestNormalObject@22a71081
com.github.hashZhang.scanfold.jdk.reference.SoftReferenceDemo$TestFinalizeObject@3930015a
java.lang.OutOfMemoryError:Java heap space
null
null
Finalize is called
GOT From Queue: null
GOT From Queue: null
Finalize is called
还有GOT From Queue: null
的先后顺序不确定,还有其他各种结果,出现这样的原因除了因为多线程,还有就是:对于软引用,一旦发生GC,并且在系统将要发生内存溢出异常之前,只要这个对象只有软引用,就会被放入队列中。放入队列后,才会真正被回收
用来描述非必须的对象,但是它的强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾收集发生之前。当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。
在java中,用java.lang.ref.WeakReference类来表示
public class WeakReferenceDemo {
public static void main(String[] args) throws InterruptedException {
ReferenceQueue testNormalObjectReferenceQueue = new ReferenceQueue<>();
ReferenceQueue testFinalizeObjectReferenceQueue = new ReferenceQueue<>();
TestNormalObject testNormalObject = new TestNormalObject();
WeakReference testNormalObjectWeakReference = new WeakReference<>(testNormalObject, testNormalObjectReferenceQueue);
TestFinalizeObject testFinalizeObject = new TestFinalizeObject();
WeakReference testFinalizeObjectWeakReference = new WeakReference<>(testFinalizeObject, testFinalizeObjectReferenceQueue);
System.out.println(testNormalObjectWeakReference);
System.out.println(testFinalizeObjectWeakReference);
//强引用去掉。让对象只剩下软引用
testNormalObject = null;
testFinalizeObject = null;
Thread thread1 = new Thread() {
@Override
public void run() {
try {
System.out.println("GOT From Queue: " + testNormalObjectReferenceQueue.remove().get());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
thread1.start();
Thread thread2 = new Thread() {
@Override
public void run() {
try {
System.out.println("GOT From Queue: " + testFinalizeObjectReferenceQueue.remove().get());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
thread2.start();
//显示请求GC,但是不一定会立刻GC
System.gc();
thread1.join();
thread2.join();
}
/**
* 第一种类。未覆盖finalize
*/
public static class TestNormalObject {
}
/**
* 第二种类。覆盖finalize的类
*/
public static class TestFinalizeObject {
@Override
protected void finalize() throws Throwable {
System.out.println("Finalize is called");
}
}
}
输出类似于:
java.lang.ref.WeakReference@6d8acf
java.lang.ref.WeakReference@182830e
Finalize is called
GOT From Queue: null
GOT From Queue: null
对于弱引用,只要这个对象只有弱引用,就会被放入队列中。放入队列后,才会真正被回收,并且是在只要有GC的时候就会被回收
虚引用比较特殊,它只用于记录一个对象是否已经被回收了。GC流程很复杂,而且Java程序员无法知道究竟一个可以被GC的对象到底有没有确定被回收。这时候,我们就需要虚引用。
虚引用和前面的软引用、弱引用不同,它并不影响对象的生命周期。在java中用java.lang.ref.PhantomReference类表示。如果一个对象与虚引用关联,则跟没有引用与之关联一样,在任何时候都可能被垃圾回收器回收。
虚引用和弱引用还有软引用不同,虚引用只有在对象的finalize方法被调用后(如果类覆盖了这个finalize方法),并且已经被回收之后,才会进入初始化时的ReferenceQueue。而另外两种引用是先进入ReferenceQueue之后对象的finalize方法被调用之后回收。
首先我们来看一下如果实现了finalize()会对GC有何影响,针对Mark-Sweep方式的GC来说,个unreachable的对象如果是finalizable的,Minor GC知道不能马上杀掉,需要先执行finalize()方法。但finalize()方法Minor GC自己又不能执行。需要Finalizer的finalizer daemon thread线程负责执行。所以Minor GC没办法,只好先把它插入到finalization queue。等以后什么时候finalizer daemon thread接手了,会一个个执行队列里对象的finalize()方法。所以,需要两轮GC才能将这个对象回收掉。
还有,如果finalize()执行时间特别长,慢于生成对象的速度,那么还会造成内存溢出的风险。
所以总之,不要用finalize()!!!