转载于 www.cnblogs.com/lwbqqyumidi…
Java一共有四种引用类型:
- 强引用 Strong reference
- 软引用 Soft Reference
- 弱引用 Weak Reference
- 虚引用 Phantom Reference
1.强引用
实际编程中,最常见的引用类型,常见形式如:A a = new A()。强引用本身存储在栈内存,指向的对象在堆内存。如果堆内存的对象不再有任何引用指向它,比如a=null,那么垃圾回收时,它会被回收。
2.软引用
软引用的一般使用形式如下:
A a = new A();
SoftReference<A> srA = new SoftReference<A>(a);
通过对象的强引用为参数,创建一个SoftReference对象,并用弱引用srA指向这个对象,此时a=null,垃圾回收时会发生什么?
import java.lang.ref.SoftReference;
public class ReferenceTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
A a = new A();
SoftReference<A> softA = new SoftReference<A>(a);
a = null;
if(softA.get() == null){
System.out.println("已经被回收....");
}else {
System.out.println("没有被回收....");
}
//垃圾回收
System.gc();
if(softA.get() == null){
System.out.println("已经被回收....");
}else {
System.out.println("没有被回收....");
}
}
}
class A{
}
输出
没有被回收....
没有被回收....
当a=null时,堆内存中的A对象将不再有任何的强引用指向它,但有软引用指向它。第一次调用soft.get()开始时没有发生gc,此时有返回值。后面强制gc后,A对象仍然没有被回收。那么,软引用所指示的对象什么时候才开始被垃圾回收呢?需要同时满足如下两个条件:
- 当其指示的对象没有任何强引用对象指向它; 2.当虚拟机内存不足
3.弱引用
形如
A a = new A();
WeakReference<A> wrA = new WeakReference<A>(a);
依旧测试上述代码
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
public class ReferenceTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
A a = new A();
WeakReference<A> softA = new WeakReference<A>(a);
a = null;
if(softA.get() == null){
System.out.println("已经被回收....");
}else {
System.out.println("没有被回收....");
}
//垃圾回收
System.gc();
if(softA.get() == null){
System.out.println("已经被回收....");
}else {
System.out.println("没有被回收....");
}
}
}
class A{
}
输出
没有被回收....
已经被回收....
WeakReference不改变原有强引用对象的垃圾回收时机,一旦其指示对象没有任何强引用对象时,此对象即进入正常的垃圾回收流程。 其主要使用场景见于:当前已有强引用指向强引用对象,此时由于业务需要,需要增加对此对象的引用,同时又不希望改变此引用的垃圾回收时机,此时WeakReference正好符合需求,常见于一些与生命周期的场景中。
对于SoftReference和WeakReference,还有一个构造构造器参数为ReferenceQueue,当SoftReference或WeakReference所指示的对象确实被垃圾回收后,其引用将被放置于ReferenceQueue中。
注意上文中,当SoftReference或WeakReference的get()方法返回null时,仅是表明其指示的对象已经进入垃圾回收流程,此时对象不一定已经被垃圾回收。而只有确认被垃圾回收后,如果ReferenceQueue,其引用才会被放置于ReferenceQueue中。
public class ReferenceTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
A a = new A();
ReferenceQueue<A> rq = new ReferenceQueue<>();
WeakReference<A> softA = new WeakReference<A>(a,rq);
a = null;
if(softA.get() == null){
System.out.println("a对象进入垃圾回收流程....");
}else {
System.out.println("没有被回收....");
}
System.out.println("rq item:" + rq.poll());
//垃圾回收
System.gc();
if(softA.get() == null){
System.out.println("a对象进入垃圾回收流程....");
}else {
System.out.println("没有被回收....");
}
System.out.println("rq item:" + rq.poll());
}
}
class A{
@Override
protected void finalize() throws Throwable {
// TODO Auto-generated method stub
super.finalize();
System.out.println("in A finalize,马上被回收");
}
}
运行结果:
没有被回收....
rq item:null
a对象进入垃圾回收流程....
rq item:null
in A finalize,马上被回收
4.虚引用
与SoftReference或WeakReference相比,PhantomReference主要差别体现在如下几点: 1.PhantomReference只有一个构造函数PhantomReference(T referent, ReferenceQueue<? super T> q),因此,PhantomReference使用必须结合ReferenceQueue; 2..不管有无强引用指向PhantomReference的指示对象,PhantomReference的get()方法返回结果都是null。
public class ReferenceTest {
public static void main(String[] args) {
A a = new A();
ReferenceQueue<A> rq = new ReferenceQueue<A>();
PhantomReference<A> prA = new PhantomReference<A>(a, rq);
System.out.println("prA.get():" + prA.get());
a = null;
System.gc();
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("rq item:" + rq.poll());
}
}
class A {
}
输出结果为:
1 prA.get():null
2 rq item:java.lang.ref.PhantomReference@1da12fc0
与WeakReference相同,PhantomReference并不会改变其指示对象的垃圾回收时机
以下是Android涉及的引用
我们在使用Handler时,如果按照下面的用法:
错误示例:
此时Android studio会把这一段标黄色,警告
Issue: Ensures that Handler classes do not hold on to a reference to an outer class
Id: HandlerLeak
Since this Handler is declared as an inner class, it may prevent the outer class from being garbage collected. If the Handler is using a Looper or MessageQueue for a thread other than the main thread, then there is no issue. If the Handler is using the Looper or MessageQueue of the main thread, you need to fix your Handler declaration, as follows: Declare the Handler as a static class;In the outer class, instantiate a WeakReference to the outer class and pass this object to your Handler when you instantiate the Handler; Make all references to members of the outer class using the WeakReference object.
大概意思是 由于handler定义为内部类,可能会阻止GC。如果handler的Looper或MessageQueue 非主线程,那么没有问题。如果handler的Looper或MessageQueue 在主线程,那么需要按如下定义:定义handler为静态内部类,当你实例化handler的时候,传入一个外部类的弱引用,以便通过弱引用使用外部类的所有成员
这里有几个点需要注意:
1.主线程Looper生命周期和Activity的生命周期一致
2.非静态内部类,或者匿名内部类。默认持有外部类引用。
handler这个匿名内部类持有外部Activity的引用,导致Activity销毁时无法释放其内存
正确使用:
- 定义为静态内部类
- 可以在Activity onDestroy()时调用handler.removeCallbacksAndMessages(null),这样就把queue里所有的message都remove掉了
下面问题来了
1.为什么匿名内部类会持有外部对象的引用,为什么静态内部类可以解决这个问题?
2.为什么在非主线程不会有这个问题?
解决这些问题的时候,看到一个系列文章,比我写的好多了,直接转载吧
zhuanlan.zhihu.com/p/78878986