type
status
date
slug
summary
tags
category
icon
password

引用类型

  • SoftReference
    • 普通的GC不会回收软引用,只有在即将发生OOM的时候(即最后一次Full GC),如果被引用的对象只有SoftReference指向的引用,才会被回收.
  • WeakReference
    • 当发生GC时,如果被引用的对象只有WeakReference指向的引用,就会被回收
  • PhantomReference
    • 不能通过虚引用获取到其关联的对象,但是当GC发生时,如果其引用的对象被回收,这个事件是可以感知的,这样就可以做相应的处理.
  • 强引用
GC是如何决定一个对象是否被回收?
从GC Root开始向下搜索,如果对象与GC之间存在引用链,则对象是可达的(reachable),GC会根据是否可达,以及对象的可达性来决定对象是否可以被回收.
而对象的可达性与引用类型相关:
  • 强可达
  • 软可达
  • 虚可达
  • 弱可达
  • 不可达

Refercence的几种状态

一个Reference对象可能处于以下4种状态中的一种:
  • Active
    • 新创建的对象都是处于这个状态;
      GC检测到引用的对象可达性改变时,会将状态转变成Pending或者Inactiive(如果refercence创建时带了referenceQueue,则会将refercence加入到pending 引用队列中)
      queue = 如果创建Refercence时注册了queue,则对应那个queue,如果没有,则为RefercenceQueue.NULL next = null
  • Pending
    • 马上要被放进队列中的对象,即即将被回收的对象
      pending引用队列中的一个元素,等着被ReferenceHandler线程(一个守护线程)去enqueue.
      没有注册(即创建时没有带queue的)的对象不会处于这个状态;
      queue = 注册时的RefercenceQueue next = this
  • Enqueued
    • 对象的内存已经被回收了,已经把这个对象放到了一个队列中
      当一个对象从它的引用队列中移除时,它就是Inactive状态了,未注册的对象不会处于这个状态;
      queue = RefercenceQueue.ENQUEUED next = queue中下一个对象,或者this(这是最后一个元素)
  • Inactive
    • 最终状态,不会再改变了.
      queue = RefercenceQueue.NULL next = this

处理流程

JVM在进行GC的时候,如果当前对象只被Reference对象引用,JVM会根据Reference的具体类型和堆内存的使用情况来决定:
是否把对应的Reference对象加入到一个由Reference构成的pending链表上(即Refercence中的next属性).
如果能加入到pending链表上,则JVM会同时通知ReferenceHandler线程进行处理.
ReferenceHandler线程是在Reference初始化的时候创建的,它是一个守护线程,并拥有最高优先级:
static { ThreadGroup tg = Thread.currentThread().getThreadGroup(); for (ThreadGroup tgn = tg; tgn != null; tg = tgn, tgn = tg.getParent()); Thread handler = new ReferenceHandler(tg, "Reference Handler"); /* If there were a special system-only priority greater than * MAX_PRIORITY, it would be used here */ handler.setPriority(Thread.MAX_PRIORITY); handler.setDaemon(true); handler.start(); // provide access in SharedSecrets SharedSecrets.setJavaLangRefAccess(new JavaLangRefAccess() { @Override public boolean tryHandlePendingReference() { return tryHandlePending(false); } }); }
再来看看ReferenceHandler:
private static class ReferenceHandler extends Thread { private static void ensureClassInitialized(Class<?> clazz) { try { Class.forName(clazz.getName(), true, clazz.getClassLoader()); } catch (ClassNotFoundException e) { throw (Error) new NoClassDefFoundError(e.getMessage()).initCause(e); } } static { ensureClassInitialized(InterruptedException.class); ensureClassInitialized(Cleaner.class); } ReferenceHandler(ThreadGroup g, String name) { super(g, name); } public void run() { while (true) { tryHandlePending(true); } } } static boolean tryHandlePending(boolean waitForNotify) { Reference<Object> r; Cleaner c; try { synchronized (lock) { if (pending != null) { r = pending; // 'instanceof' might throw OutOfMemoryError sometimes // so do this before un-linking 'r' from the 'pending' chain... c = r instanceof Cleaner ? (Cleaner) r : null; // unlink 'r' from 'pending' chain pending = r.discovered; r.discovered = null; } else { // The waiting on the lock may cause an OutOfMemoryError // because it may try to allocate exception objects. if (waitForNotify) { lock.wait(); } // retry if waited return waitForNotify; } } } catch (OutOfMemoryError x) { // Give other threads CPU time so they hopefully drop some live references // and GC reclaims some space. // Also prevent CPU intensive spinning in case 'r instanceof Cleaner' above // persistently throws OOME for some time... Thread.yield(); // retry return true; } catch (InterruptedException x) { // retry return true; } // Fast path for cleaners if (c != null) { c.clean(); return true; } ReferenceQueue<? super Object> q = r.queue; if (q != ReferenceQueue.NULL) q.enqueue(r); return true; }
ReferenceHandler线程内部的run方法,会不断从pending链表中获取Refercence对象,
如果能获取到,则根据不同的引用类型进行处理;
如果获取不到,则调用wait方法等待GC回收对象,处理pending链表的通知.

参考文章

  1. http://ifeve.com/java-reference核心原理分析/
Context.getSystemService()实现解析Android APP启动流程分析
姜康
姜康
一个软件工程师
公告
type
status
date
slug
summary
tags
category
icon
password
🎉博客网站重新制作了🎉
👏欢迎更新体验👏