[关闭]
@universal 2018-05-22T08:09:37.000000Z 字数 3314 阅读 324

强引用、弱引用、软引用、虚引用

java


首先还是从问题入手:这个四个引用有什么区别,具体使用场景是什么?

在Java中,除了基本数据类型的变量(int ,double等),其他都是引用类型,指向不同的对象(类、接口或者数组等复杂的数据结构)。在这里拓展一下,浅拷贝和深拷贝的区别就是在clone对象时,是否clone引用类型的数据变量(记个小笔记)。


其实不同的引用类型的具体作用体现在JVM中,主要影响对象的可达性和GC回收。

强引用:

常见的普通引用,像Class c =new Class();c就是一个强引用指向Class对象。只要还有强引用指向对象,那就表明对象还活着,当JVM内存不足时,JVM宁愿OOM,使程序终止,也不会回收具有强引用的对象;相反对于一个普通对象,如果没有其他的引用指向该对象,只要超过了引用的作用域(比如作用域为方法级或者块级,程序运行完了方法后)或者显式地将强引用赋值为null,GC就会回收该对象。

软引用:

相对强引用弱化一些的引用,可以让对象避免一些垃圾回收。只有当JVM判定内存不足时,才会去回收软引用指向的对象:即JVM确保在发生OOM之前,会去清理软引用对象。
软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收器回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。后续,我们可以调用ReferenceQueue的poll()方法来检查是否有它所关心的对象被回收。如果队列为空,将返回一个null,否则该方法返回队列中前面的一个Reference对象
软引用通常用来实现内存敏感的缓存,如果还有空闲内存,就可以暂时保留缓存,当内存不足时清理掉,这样就保证了使用缓存的同时,不会耗尽内存。(例如图片缓存)

弱引用:

弱引用并不能是对象避免垃圾收集,仅仅提供了一种访问对象的途径。这就可以用来构建一种没有特定约束的关系,如果试图获取的对象还在,就使用它,否则重新实例化。弱引用同样可以和一个引用队列联合使用。
弱引用同样是很多缓存实现的选择。

虚引用:

虚引用有的地方也翻译成幻象引用。不能通过它去访问对象,虚引用仅仅提供了一种确保对象被finalize后,做某些事情的机制。比如Post-Mortem 清理机制。当为对象设置虚引用时,该对象被GC回收时,会收到一个系统通知,从而来跟踪对象的GC回收情况。


(1). 所有的引用类型都是继承自抽象类 java.lang.ref.Reference;看看里面的get方法

  1. /**
  2. * Returns this reference object's referent. If this reference object has
  3. * been cleared, either by the program or by the garbage collector, then
  4. * this method returns <code>null</code>.
  5. */
  6. public T get() {
  7. return this.referent;
  8. }

可以看到,如果对象没有被销毁,就可以通过get方法获取到原有对象当然虚引用除外(因为它的get返回null),所以我们可以将通过软引用和弱引用访问到的对象重新指向强引用,也就是认为的改变对象的可达性状态。因此对于软引用、弱引用之类,垃圾收集器可能会存在二次确认的问题,以保证处于弱引用状态的对象,没有改变为强引用。

(2). 引用队列(ReferenceQueue)
我们在创建各种引用并关联到响应对象时,可以选择是否需要关联引用队列,JVM 会在特定时机将引用 enqueue 到队列里,我们可以从队列里获取引用进行相关后续逻辑。尤其是幻象引用,get 方法只返回 null,如果再不指定引用队列,基本就没有意义了。

  1. Object object = new Object();
  2. ReferenceQueue refQueue = new ReferenceQueue<>();
  3. PhantomReference<Object> p = new PhantomReference<>(object, refQueue);
  4. object = null;
  5. System.gc();
  6. try {
  7. // Remove 是一个阻塞方法,可以指定 timeout,或者选择一直阻塞
  8. Reference<Object> ref = refQueue.remove(1000L);
  9. if (ref != null) {
  10. // do something
  11. }
  12. } catch (InterruptedException e) {
  13. // Handle it
  14. }

gc准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。利用引用队列,我们可以在对象处于相应状态时(对于幻象引用,就是前面说的被 finalize 了,处于幻象可达状态),执行后期处理逻辑。

(3). 拓展一种特殊情况:通常情况下,如果第一个对象没有指向强引用就符合GC回收标准,但是有时候对象本身并没有强引用,但是它的部分属性还在被使用。所以需要一个方法,在没有强引用情况下,通知JVM对象是在被使用的。在JAVA 9中可以按照下面代码来实现:

  1. class Resource {
  2. private static ExternalResource[] externalResourceArray = ...
  3. int myIndex;
  4. //构造
  5. Resource(...) {
  6. myIndex = ...
  7. externalResourceArray[myIndex] = ...;
  8. ...
  9. }
  10. protected void finalize() {
  11. externalResourceArray[myIndex] = null;
  12. ...
  13. }
  14. public void action() {
  15. try {
  16. // 需要被保护的代码
  17. int i = myIndex;
  18. Resource.update(externalResourceArray[i]);
  19. } finally {
  20. // 调用 reachbilityFence,明确保障对象 strongly reachable
  21. Reference.reachabilityFence(this);
  22. }
  23. }
  24. private static void update(ExternalResource ext) {
  25. ext.status = ...;
  26. }
  27. }

action()执行依赖对象的部分属性,假如我们直接

  1. new Resource().action()//这种写法在异步编程中较为常见

因为没有强引用指向Resource对象,所以JVM对它进行finalize是合法的,因此需要利用reachbilityFence方法保护该对象,声明该对象为强可达。


android中具体的应用:利用软、弱引用防止内存泄漏

常用的比如handler、AsyncTask等。
改进的handler写法:(利用静态内部类和弱引用防止内存泄漏)

  1. public class MainActivity extends Activity {
  2. @Override
  3. protected void onCreate(Bundle savedInstanceState) {
  4. super.onCreate(savedInstanceState);
  5. mHandler.sendMessageDelayed(mMesssage, 1000 * 60 * 10);
  6. finish();
  7. }
  8. private static class MyHandler extends Handler {
  9. private final WeakReference<MainActivity> mActivity;
  10. public MyHandler(MainActivity activity) {
  11. mActivity = new WeakReference<MainActivity>(activity);
  12. }
  13. @Override
  14. public void handleMessage(Message msg) {
  15. MainActivity activity = mActivity.get();
  16. if (activity != null) {
  17. // do something
  18. }
  19. }
  20. }
  21. private final MyHandler mHandler = new MyHandler(this);
  22. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注