文档章节

【转载】Android 内存泄漏检查工具LeakCanary源碼浅析

beijing_zbs
 beijing_zbs
发布于 2016/08/12 17:39
字数 4176
阅读 240
收藏 2
点赞 0
评论 0

[摘要:应用 参考我之前写的《Android 内存泄露对象应用》 监控 Activity 泄漏 我们常常把 Activity 看成为 Context 工具应用,正在分歧场所由种种工具援用 Activity。以是,] 

http://www.jianshu.com/p/0049e9b344b0

使用

参考我之前写的《Android 内存泄漏工具使用》

监控 Activity 泄露

我们经常把 Activity 当作为 Context 对象使用,在不同场合由各种对象引用 Activity。所以,Activity 泄漏是一个重要的需要检查的内存泄漏之一。

public class ExampleApplication extends Application {
    public static RefWatcher getRefWatcher(Context context) {
        ExampleApplication application = (ExampleApplication) context.getApplicationContext();
        return application.refWatcher;
    }

    private RefWatcher refWatcher;
    @Override public void onCreate() {
        super.onCreate();
        refWatcher = LeakCanary.install(this);
    }
}

LeakCanary.install() 返回一个配置好了的 RefWatcher 实例。它同时安装了 ActivityRefWatcher 来监控 Activity 泄漏。即当 Activity.onDestroy() 被调用之后,如果这个 Activity 没有被销毁,logcat 就会打印出信息告诉你内存泄漏发生了。

leakInfo:In com.example.leakcanary:1.0:1.
* com.example.leakcanary.MainActivity has leaked:
* GC ROOT thread java.lang.Thread.<Java Local> (named 'AsyncTask #1')
* references com.example.leakcanary.MainActivity$2.this$0 (anonymous subclass of android.os.AsyncTask)
* leaks com.example.leakcanary.MainActivity instance                               * 

LeakCanary 自动检测 Activity 泄漏只支持 Android ICS 以上版本。因为 Application.registerActivityLifecycleCallbacks() 是在 API 14 引入的。如果要在 ICS 之前监测 Activity 泄漏,可以重载 Activity.onDestroy() 方法,然后在这个方法里调用 RefWatcher.watch(this) 来实现。

监控 Fragment 泄漏

public abstract class BaseFragment extends Fragment {

    @Override 
    public void onDestroy() {
        super.onDestroy();
        RefWatcher refWatcher = ExampleApplication.getRefWatcher(getActivity());
        refWatcher.watch(this);
    }
}

当 Fragment.onDestroy() 被调用之后,如果这个 fragment 实例没有被销毁,那么就会从 logcat 里看到相应的泄漏信息。

监控其他泄漏

    RefWatcher refWatcher = ExampleApplication.getRefWatcher(getActivity());
    refWatcher.watch(someObjNeedGced);

当 someObjNeedGced 还在内存中时,就会在 logcat 里看到内存泄漏的提示。

原理

\
1. RefWatcher.watch() 创建一个 KeyedWeakReference 到要被监控的对象。 
2. 然后在后台线程检查引用是否被清除,如果没有,调用GC。 
3. 如果引用还是未被清除,把 heap 内存 dump 到 APP 对应的文件系统中的一个 .hprof 文件中。 
4. 在另外一个进程中的 HeapAnalyzerService 有一个 HeapAnalyzer 使用 另一个开源库HAHA 解析这个hprof文件。(MAT也可以解析hprof,但是是图形化的形式,HAHA完全是非UI形式解析) 
5. 得益于唯一的 reference key, HeapAnalyzer 找到 KeyedWeakReference,定位内存泄露。 
6. HeapAnalyzer 计算 到 GC roots 的最短强引用路径,并确定是否是泄露。如果是的话,建立导致泄露的引用链。 
7. 引用链传递到 APP 进程中的 DisplayLeakService, 并以通知的形式展示出来。

现在就来通过代码来讲解吧 
首先,Application的onCreate方法中调用LeakCanary.install(this);做初始化工作 
LeakCanary.java

  public static RefWatcher install(Application application) {
    return install(application, DisplayLeakService.class,
        AndroidExcludedRefs.createAppDefaults().build());
  }
  public static RefWatcher install(Application application,
      Class<? extends AbstractAnalysisResultService> listenerServiceClass,
      ExcludedRefs excludedRefs) {
    //首先判断当前应用程序进程是否在HeapAnalyzerService所在的进程,如果是直接return,HeapAnalyzerService所在的进程是HAHA库专门解析hprof文件的进程
    if (isInAnalyzerProcess(application)) {
      return RefWatcher.DISABLED;
    }
    enableDisplayLeakActivity(application);
    HeapDump.Listener heapDumpListener =
        new ServiceHeapDumpListener(application, listenerServiceClass); //listenerServiceClass就是上面的DisplayLeakService.class,用来显示内存泄漏信息用的
    RefWatcher refWatcher = androidWatcher(application, heapDumpListener, excludedRefs); //构造RefWatcher类对象
    ActivityRefWatcher.installOnIcsPlus(application, refWatcher);  //调用ActivityRefWatcher的方法
    return refWatcher;
  }
//ActivityRefWatcher类,专门针对activity,所以在api14以上的onDestroy中不用监控,自动回调
@TargetApi(ICE_CREAM_SANDWICH) public final class ActivityRefWatcher {
  private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
      new Application.ActivityLifecycleCallbacks() {
        ..............
        @Override public void onActivityDestroyed(Activity activity) {
          ActivityRefWatcher.this.onActivityDestroyed(activity); //当activity退出执行了onDestory方法的时候就会执行该onActivityDestroyed方法
        }
      };

  private final Application application;
  private final RefWatcher refWatcher;

  void onActivityDestroyed(Activity activity) {
    refWatcher.watch(activity); //调用watch方法
  }

  public void watchActivities() {
    // Make sure you don't get installed twice.
    stopWatchingActivities();
    application.registerActivityLifecycleCallbacks(lifecycleCallbacks); //api14才引入的方法,任何activity生命周期的变化都会回调给lifecycleCallbacks
  }

  public void stopWatchingActivities() {
    application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks);
  }
}

excludedRefs 
/* References that should be ignored when analyzing this heap dump. 
SDK导致的内存泄露,随着时间的推移,很多SDK 和厂商 ROM 中的内存泄露问题已经被尽快修复了。但是,当这样的问题发生时,一般的开发者能做的事情很有限。LeakCanary有一个已知问题的忽略列表,AndroidExcludedRefs.java中可以查看到 
就是说如果发现AndroidExcludedRefs.java类中维护的列表类的内存泄漏,那么在DisplayLeakService并不会显示出来,同时HeapAnalyzer在计算到GC roots的最短强引用路径,也会忽略这些类 
下面具体分析。

此时初始化完毕,如果退出一个activity那么就会执行refWatcher.watch(activity); 
RefWatcher.java

  public void watch(Object watchedReference, String referenceName) {
    checkNotNull(watchedReference, "watchedReference");
    checkNotNull(referenceName, "referenceName");
    if (debuggerControl.isDebuggerAttached()) { //连上数据线就此时就返回true了
      return;
    }
    final long watchStartNanoTime = System.nanoTime();
    String key = UUID.randomUUID().toString(); //唯一的key
    retainedKeys.add(key); //key保存在retainedKeys集合
    final KeyedWeakReference reference =
        new KeyedWeakReference(watchedReference, key, referenceName, queue); //虚引用KeyedWeakReference

    watchExecutor.execute(new Runnable() { //watchExecutor就不说了,自定义的一个Executor
        @Override
        public void run() {
            ensureGone(reference, watchStartNanoTime); //关键ensureGone方法
        }
    });
  }
  void ensureGone(KeyedWeakReference reference, long watchStartNanoTime) {
    long gcStartNanoTime = System.nanoTime();
    long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);
    removeWeaklyReachableReferences();
    if (gone(reference) || debuggerControl.isDebuggerAttached()) { //判断reference是否被回收了已经为null,同时是否插了USB线,调试模式下,不去检查内存泄漏
      return;
    }
    gcTrigger.runGc(); //触发GC
    removeWeaklyReachableReferences();
    if (!gone(reference)) { //哈,此时说明强制发生GC,该引用还在,那么就没被回收咯,可能发生内存泄漏,所以才能继续往下走
      long startDumpHeap = System.nanoTime();
      long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime);

      File heapDumpFile = heapDumper.dumpHeap(); //关键,生成dropf文件

      if (heapDumpFile == HeapDumper.NO_DUMP) { //NO_DUMP类型,木有生成dropf吧,直接return
        // Could not dump the heap, abort.
        return;
      }
      long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap);
      heapdumpListener.analyze(
          new HeapDump(heapDumpFile, reference.key, reference.name, excludedRefs, watchDurationMs,
              gcDurationMs, heapDumpDurationMs)); //heapdumpListener就是上面初始化的ServiceHeapDumpListener
    }
  }

  private boolean gone(KeyedWeakReference reference) {
    //當referent被回收了,因為removeWeaklyReachableReferences方法所以該key被移出了,contains返回false,gone返回true
    //當referent沒被回收了,因為key還在retainedKeys中,contains返回true,gone返回false
    return !retainedKeys.contains(reference.key);
  }

  private void removeWeaklyReachableReferences() {
    // WeakReferences are enqueued as soon as the object to which they point to becomes weakly
    // reachable. This is before finalization or garbage collection has actually happened.
    KeyedWeakReference ref;
    //ReferenceQueue的poll方法,當referent被回收了那麼poll方法返回不為null,沒被GC回收才為null
    while ((ref = (KeyedWeakReference) queue.poll()) != null) { 
      retainedKeys.remove(ref.key); //當referent被回收了,那麼從retainedKeys移出該key
    }
  }

先稍微看下KeyedWeakReference的定義,繼承自WeakReference

final class KeyedWeakReference extends WeakReference<Object> {
  public final String key;
  public final String name;

  KeyedWeakReference(Object referent, String key, String name,
      ReferenceQueue<Object> referenceQueue) {
    super(checkNotNull(referent, "referent"), checkNotNull(referenceQueue, "referenceQueue"));
    this.key = checkNotNull(key, "key");
    this.name = checkNotNull(name, "name");
  }
}

測試一下

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        WeakReference w = new WeakReference(new String("haha"), queue);
        System.gc();
        if (w.get() == null) {
            System.out.println("w get is null");
        } else {
            System.out.println("w get is not null");
        }
        WeakReference  temp= (WeakReference) queue.poll();
        if(temp != null) { // ReferenceQueue的poll方法
            System.out.println("queue.poll is not null");
        }else{
            System.out.println("queue.poll is null");
        }
    }

打印 
w get is null 
queue.poll is not null 
註釋掉System.gc();此時打印 
w get is not null 
queue.poll is null

然後看看heapDumper.dumpHeap()怎么生成hprof文件,然后再看heapdumpListener.analyze流程 
AndroidHeapDumper.java

  @Override public File dumpHeap() {
    if (!leakDirectoryProvider.isLeakStorageWritable()) {
      CanaryLog.d("Could not write to leak storage to dump heap.");
      leakDirectoryProvider.requestWritePermission();
      return NO_DUMP;
    }
    File heapDumpFile = getHeapDumpFile();
    // Atomic way to check for existence & create the file if it doesn't exist.
    // Prevents several processes in the same app to attempt a heapdump at the same time.
    boolean fileCreated;
    try {
      fileCreated = heapDumpFile.createNewFile();
    } catch (IOException e) {
      cleanup();
      CanaryLog.d(e, "Could not check if heap dump file exists");
      return NO_DUMP;
    }

    if (!fileCreated) {
      CanaryLog.d("Could not dump heap, previous analysis still is in progress.");
      // Heap analysis in progress, let's not put too much pressure on the device.
      return NO_DUMP;
    }

    FutureResult<Toast> waitingForToast = new FutureResult<>(); //在生成heap dump文件的时候会弹出一个toast提示界面
    showToast(waitingForToast);

    if (!waitingForToast.wait(5, SECONDS)) {
      CanaryLog.d("Did not dump heap, too much time waiting for Toast.");
      return NO_DUMP;
    }

    Toast toast = waitingForToast.get();
    try {
      Debug.dumpHprofData(heapDumpFile.getAbsolutePath()); //Debug.dumpHprofData方法生成hprof并写入该file,其实DDMS获取hprof文件应该也是调用该方法的
      cancelToast(toast);
      return heapDumpFile;
    } catch (Exception e) {
      cleanup();
      CanaryLog.d(e, "Could not perform heap dump");
      // Abort heap dump
      return NO_DUMP;
    }
  }

hprof文件默认放在/sdcard/Download/…/目录下,所以如果要使用MAT分析的话,可以到该目录下寻找,该hprof不能直接被MAT查看,需要android提供的hprof-conv转换

生成了hprof文件之后,执行heapdumpListener.analyze流程,主要就是通过另外一个进程中的HeapAnalyzerService有一个HeapAnalyzer(内部实际调用开源库HAHA)解析这个hprof文件,然后如果需要就显示在传递到APP进程中的DisplayLeakService,并以通知的形式展示出来,代码如下

HeapAnalyzerService.runAnalysis(context, heapDump, listenerServiceClass);
public final class HeapAnalyzerService extends IntentService {
  public static void runAnalysis(Context context, HeapDump heapDump,
      Class<? extends AbstractAnalysisResultService> listenerServiceClass) { //静态方法
    Intent intent = new Intent(context, HeapAnalyzerService.class); //启动本服务,此时HeapAnalyzerService运行在leakcanary独立进程中
    intent.putExtra(LISTENER_CLASS_EXTRA, listenerServiceClass.getName()); //HeapAnalyzerService
    intent.putExtra(HEAPDUMP_EXTRA, heapDump); //hropf文件
    context.startService(intent);
  }

  @Override protected void onHandleIntent(Intent intent) {
    if (intent == null) {
      CanaryLog.d("HeapAnalyzerService received a null intent, ignoring.");
      return;
    }
    String listenerClassName = intent.getStringExtra(LISTENER_CLASS_EXTRA); //DisplayLeakService
    HeapDump heapDump = (HeapDump) intent.getSerializableExtra(HEAPDUMP_EXTRA); //hropf文件

    HeapAnalyzer heapAnalyzer = new HeapAnalyzer(heapDump.excludedRefs);
    //HeapAnalyzer.checkForLeak解析这个hprof文件完毕,那么发送给app进程的HeapAnalyzerService显示通知
    AnalysisResult result = heapAnalyzer.checkForLeak(heapDump.heapDumpFile, heapDump.referenceKey);
    AbstractAnalysisResultService.sendResultToListener(this, listenerClassName, heapDump, result);
  }
}
<service
   android:name="com.squareup.leakcanary.internal.HeapAnalyzerService"
   android:enabled="false"
   android:process=":leakcanary" />

分析hprof文件,设计IO操作,放在leakcanary独立进程当然更好啦,此时没有不是单独开线程的方式 
HeapAnalyzer.java

public final class HeapAnalyzer {
  private final ExcludedRefs excludedRefs;
  public HeapAnalyzer(ExcludedRefs excludedRefs) {
    this.excludedRefs = excludedRefs;
  }
  public AnalysisResult checkForLeak(File heapDumpFile, String referenceKey) {
    ..........
    try {
      //开始解析hropf,HAHA库中的类HprofBuffer,HprofParser,Snapshot
      HprofBuffer buffer = new MemoryMappedFileBuffer(heapDumpFile); 
      HprofParser parser = new HprofParser(buffer); 
      Snapshot snapshot = parser.parse();

      Instance leakingRef = findLeakingReference(referenceKey, snapshot); //findLeakingReference检查该对象是否

      // False alarm, weak reference was cleared in between key check and heap dump.
      if (leakingRef == null) { //为null说明已经被GC回收啦,那么直接return noLeak类型,DisplayLeakService就不显示啦
        return noLeak(since(analysisStartNanoTime));
      }

      return findLeakTrace(analysisStartNanoTime, snapshot, leakingRef); //走到这里,就是计算到GC roots的最短强引用路径,并确定是否是泄露。如果是的话,建立导致泄露的引用链
    } catch (Throwable e) {
      return failure(e, since(analysisStartNanoTime));
    }
  }

  private Instance findLeakingReference(String key, Snapshot snapshot) {
    ClassObj refClass = snapshot.findClass(KeyedWeakReference.class.getName()); //HAHA类库snapshot的findclass方法
    List<String> keysFound = new ArrayList<>();
    for (Instance instance : refClass.getInstancesList()) {
      List<ClassInstance.FieldValue> values = classInstanceValues(instance);
      String keyCandidate = asString(fieldValue(values, "key"));
      if (keyCandidate.equals(key)) {
        return fieldValue(values, "referent");
      }
      keysFound.add(keyCandidate);
    }
    throw new IllegalStateException(
        "Could not find weak reference with key " + key + " in " + keysFound);
  }
  //计算到GC roots的最短强引用路径,并确定是否是泄露。如果是的话,建立导致泄露的引用链
  private AnalysisResult findLeakTrace(long analysisStartNanoTime, Snapshot snapshot,
      Instance leakingRef) {

    ShortestPathFinder pathFinder = new ShortestPathFinder(excludedRefs); //到GCroot最短路劲ShortestPathFinder,此时排除了SDK自带问题的excludedRefs
    ShortestPathFinder.Result result = pathFinder.findPath(snapshot, leakingRef);

    // False alarm, no strong reference path to GC Roots.
    if (result.leakingNode == null) {
      return noLeak(since(analysisStartNanoTime));
    }

    LeakTrace leakTrace = buildLeakTrace(result.leakingNode); //buildLeakTrace方法建立导致泄露的引用链

    String className = leakingRef.getClassObj().getClassName();

    // Side effect: computes retained size.
    snapshot.computeDominators();

    Instance leakingInstance = result.leakingNode.instance;

    long retainedSize = leakingInstance.getTotalRetainedSize();

    retainedSize += computeIgnoredBitmapRetainedSize(snapshot, leakingInstance);

    return leakDetected(result.excludingKnownLeaks, className, leakTrace, retainedSize,
        since(analysisStartNanoTime));
  }
  private LeakTrace buildLeakTrace(LeakNode leakingNode) //建立导致泄露的引用链
  private LeakTraceElement buildLeakElement(LeakNode node)  //建立导致泄露的引用链元素

最后来看下app进程中的DisplayLeakService用来显示内存泄漏的引用链的IntentService

public class DisplayLeakService extends AbstractAnalysisResultService {
  @Override protected final void onHeapAnalyzed(HeapDump heapDump, AnalysisResult result) {
    String leakInfo = leakInfo(this, heapDump, result, true);
    CanaryLog.d(leakInfo);
    boolean resultSaved = false;
    boolean shouldSaveResult = result.leakFound || result.failure != null; //leakFound表示木有内存泄漏
    if (shouldSaveResult) {
      heapDump = renameHeapdump(heapDump);
      resultSaved = saveResult(heapDump, result);
    }

    PendingIntent pendingIntent;
    String contentTitle;
    String contentText;

    if (!shouldSaveResult) {
      contentTitle = getString(R.string.leak_canary_no_leak_title);
      contentText = getString(R.string.leak_canary_no_leak_text);
      pendingIntent = null;
    } else if (resultSaved) {
      pendingIntent = DisplayLeakActivity.createPendingIntent(this, heapDump.referenceKey);

      if (result.failure == null) {
        String size = formatShortFileSize(this, result.retainedHeapSize);
        String className = classSimpleName(result.className);
        if (result.excludedLeak) { //注意这里的excludedLeak
          contentTitle = getString(R.string.leak_canary_leak_excluded, className, size);
        } else {
          contentTitle = getString(R.string.leak_canary_class_has_leaked, className, size);
        }
      } else {
        contentTitle = getString(R.string.leak_canary_analysis_failed);
      }
      contentText = getString(R.string.leak_canary_notification_message);
    } else {
      contentTitle = getString(R.string.leak_canary_could_not_save_title);
      contentText = getString(R.string.leak_canary_could_not_save_text);
      pendingIntent = null;
    }
    showNotification(this, contentTitle, contentText, pendingIntent);
    afterDefaultHandling(heapDump, result, leakInfo);
  }
  .....
}

第一行的leakInfo打印结果:

leakInfo:In com.example.leakcanary:1.0:1.
* com.example.leakcanary.MainActivity has leaked:
* GC ROOT thread java.lang.Thread.<Java Local> (named 'AsyncTask #1')
* references com.example.leakcanary.MainActivity$2.this$0 (anonymous subclass of android.os.AsyncTask)
* leaks com.example.leakcanary.MainActivity instance 

至此分析完毕

GC回收算法

hprof文件转换MAT查看

Debug.dumpHprofData方法生成hprof不能直接在MAT中查看,需要使用android提供的hprof-conv转换

hprof-conv xxxxx.hprof yyyyy.hprof,其中xxxxx.hprof为原始文件,yyyyy.hprof为转换过后的文件

根搜索算法

参考:根搜索算法 
在主流的商用程序语言中(Java和C#,甚至包括前面提到的古老的Lisp),都是使用根搜索算法(GC Roots Tracing)判定对象是否存活的。这个算法的基本思路就是通过一系列的名为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连(用图论的话来说就是从GC Roots到这个对象不可达)时,则证明此对象是不可用的。如图3-1所示,对象object 5、object 6、object 7虽然互相有关联,但是它们到GC Roots是不可达的,所以它们将会被判定为是可回收的对象。 
在Java语言里,可作为GC Roots的对象包括下面几种: 
1. 虚拟机栈(栈帧中的本地变量表)中的引用的对象。 
2. 方法区中的类静态属性引用的对象。 
3. 方法区中的常量引用的对象。 
4. 本地方法栈中JNI(即一般说的Native方法)的引用的对象。 
\

无论是通过引用计数算法判断对象的引用数量,还是通过根搜索算法判断对象的引用链是否可达,判定对象是否存活都与“引用”有关。在JDK 1.2之前,Java中的引用的定义很传统:如果reference类型的数据中存储的数值代表的是另外一块内存的起始地址,就称这块内存代表着一个引用。这种定义很纯粹,但是太过狭隘,一个对象在这种定义下只有被引用或者没有被引用两种状态,对于如何描述一些“食之无味,弃之可惜”的对象就显得无能为力。我们希望能描述这样一类对象:当内存空间还足够时,则能保留在内存之中;如果内存在进行垃圾收集后还是非常紧张,则可以抛弃这些对象。很多系统的缓存功能都符合这样的应用场景。

在JDK 1.2之后,Java对引用的概念进行了扩充,将引用分为强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)、虚引用(Phantom Reference)四种,这四种引用强度依次逐渐减弱。 
1. 强引用就是指在程序代码之中普遍存在的,类似“Object obj = new Object()”这类的引用,只要强引用还存在,垃圾收集器永远不会回收掉被引用的对象。 
2. 软引用用来描述一些还有用,但并非必需的对象。对于软引用关联着的对象,在系统将要发生内存溢出异常之前,将会把这些对象列进回收范围之中并进行第二次回收。如果这次回收还是没有足够的内存,才会抛出内存溢出异常。在JDK 1.2之后,提供了SoftReference类来实现软引用。 
3. 弱引用也是用来描述非必需对象的,但是它的强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾收集发生之前。当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。在JDK 1.2之后,提供了WeakReference类来实现弱引用。 
4. 虚引用也称为幽灵引用或者幻影引用,它是最弱的一种引用关系。一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的就是希望能在这个对象被收集器回收时收到一个系统通知。在JDK 1.2之后,提供了PhantomReference类来实现虚引用。

引用计数法

引用计数法是唯一没有使用根集的垃圾回收的法,该算法使用引用计数器来区分存活对象和不再使用的对象。一般来说,堆中的每个对象对应一个引用计数器。当每一次创建一个对象并赋给一个变量时,引用计数器置为1。当对象被赋给任意变量时,引用计数器每次加1当对象出了作用域后(该对象丢弃不再使用),引用计数器减1,一旦引用计数器为0,对象就满足了垃圾收集的条件。 
基于引用计数器的垃圾收集器运行较快,不会长时间中断程序执行,适宜地必须 实时运行的程序。但引用计数器增加了程序执行的开销,因为每次对象赋给新的变量,计数器加1,而每次现有对象出了作用域生,计数器减1。 
ps:用根集的方法(既有向图的方法)进行内存对象管理,可以消除循环引用的问题.就是说如果有三个对象相互引用,只要他们和根集是不可达的,gc也是可以回收他们.根集的方法精度很高,但是效率低.计数器法精度低(无法处理循环引用),但是执行效率高.

本文转载自:http://www.ithao123.cn/content-10798079.html

共有 人打赏支持
beijing_zbs
粉丝 7
博文 150
码字总数 31629
作品 0
塘沽
QA/测试工程师
Android内存优化(六)LeakCanary使用详解

相关文章 Android性能优化系列 Java虚拟机系列 1.概述 如果使用MAT来分析内存问题,会有一些难度,并且效率也不是很高,对于一个内存泄漏问题,可能要进行多次排查和对比。 为了能够简单迅速...

刘望舒
2017/09/10
0
0
LeakCanary——消除Android中的内存泄露

LeakCanary 简介 LeakCanary是Square公司最近公布的开源项目,旨在消除Android中的内存泄露(Memory Leak),项目地址猛戳这里。 使用 LeakCanary的使用也非常之简单,仅需要在Application中...

iuKa
2015/05/11
0
0
内存泄露和LeakCanary的故事

新鲜文章请关注微信公众号: JueCode 今天我们来聊一聊Android中的内存泄露和内存泄露的检测工具LeakCanary。Java有垃圾回收线程为什么还会发生内存泄露?原因就是外部人为持有对象引用,持有...

juexingzhe
05/22
0
0
Android应用内存泄漏的定位、分析与解决策略

Hello,大家好,我是Clock。翻了一下简书,发现有一个多月没有更新博客,本来今天打算和妹纸去电影院看《你的名字》,然后再去到处浪的。 结果因为妹纸公司临时有事,她不得不回公司一趟......

D_clock爱吃葱花
06/29
0
0
一步步拆解 LeakCanary

本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布 java 源码系列 - 带你读懂 Reference 和 ReferenceQueue https://blog.csdn.net/gdutxiaoxu/article/details/80738581 一步步拆解 ...

xujun9411
07/04
0
0
30多个Android 开发者工具 带你开发带你飞

FlowUp 这是一个帮助你跟踪app整体性能的工具,深入分析关键的性能数据如FPS, 内存, CPU, 磁盘, 等等。FlowUp根据用户数量收费。 Stetho 由Facebook开发的一个强大的开源Android debug平台,...

猴亮屏
2017/10/30
0
0
Android studio用的几个插件

android studio常用插件,可极大简化开发,增强开发效率。 不懂安装studio插件,看参考博文:android stuido插件装:http://my.oschina.net/kooeasy/blog/491995 1、ButterKnife Zelezny But...

让代码飞一会
2015/08/13
0
1
Android内存优化之LeakCanary

LeakCanary是检查内存泄漏的神器 虽然我没检查出来啥内存泄漏 是我代码写的ok还是我的打开方式不正确? 不管怎么说 在app上线前检查一下内存泄漏 是必要的工作 学习自 https://blog.csdn.ne...

qq_36523667
05/02
0
0
Android中的内存泄漏和内存溢出.md

概念 内存泄漏是因为持有对象长期引用,导致对象无法被 GC 回收。 为了避免这种情况,我们可以选择在对象生命周期结束的时候,解除绑定,将引用置为空,或者使用弱引用。 例子 单例模式导致内...

hongjay
05/09
0
0
【掘金小报】 第十五期 用 slack 和 hubot 搭建你自己的运维机器人

掘金小报主打分享优质深度技术内容,技术内容分:前端、后端、Android、iOS、产品设计、工具资源和一些有趣的东西。 与标题相关文章为最后一篇文章。 【译】带你入门 CSS Grid 布局 三月中旬...

膜法小编
2017/05/16
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

面试系列-40个Java多线程问题总结

前言 这篇文章主要是对多线程的问题进行总结的,因此罗列了40个多线程的问题。 这些多线程的问题,有些来源于各大网站、有些来源于自己的思考。可能有些问题网上有、可能有些问题对应的答案也...

Ryan-瑞恩
9分钟前
0
0
微信分享的细节

分享的缩略图要求: 一、图片大小小于32k 二、图片的尺寸为 宽度 :128px 高度:128px 分享title 和 description 出现金额等 以上情况存在会导致触发分享按钮 但是页面没有反应...

Js_Mei
15分钟前
0
0
【2018.07.23学习笔记】【linux高级知识 Shell脚本编程练习】

1、编写shell脚本,计算1-100的和; #!/bin/bashsum=0for i in `seq 1 100`do sum=$[$sum+$i]doneecho $sum 2、编写shell脚本,要求输入一个数字,然后计算出从1到输入数字的和,要求...

lgsxp
17分钟前
0
0
xss攻防浅谈

导读 XSS (Cross-Site Script) 攻击又叫跨站脚本攻击, 本质是一种注入攻击. 其原理, 简单的说就是利用各种手段把恶意代码添加到网页中, 并让受害者执行这段脚本. XSS能做用户使用浏览器能做的...

吴伟祥
17分钟前
0
0
js回调的一次应用

function hideBtn(option) { if (option == 1) { $("#addBtn").hide(); $("#addSonBtn").hide(); }}$("body").on("click", "#selectBtn", function () {......

晨猫
24分钟前
0
0
C++_读写ini配置文件

1.WritePrivateProfileString:

一个小妞
24分钟前
0
0
通往阿里,BAT的50+经典Java面试题及答案解析(上)

Java是一个支持并发、基于类和面向对象的计算机编程语言。下面列出了面向对象软件开发的优点: 代码开发模块化,更易维护和修改。 代码复用。 增强代码的可靠性和灵活性。 增加代码的可理解性...

Java大蜗牛
24分钟前
1
0
数据库两大神器【索引和锁】

前言 只有光头才能变强 索引和锁在数据库中可以说是非常重要的知识点了,在面试中也会经常会被问到的。 本文力求简单讲清每个知识点,希望大家看完能有所收获 声明:如果没有说明具体的数据库...

Java3y
28分钟前
0
0
Application Express安装

Application Express安装文档 数据库选择和安装 数据库选择 Oracle建议直接12.2.0.1.0及以上的版本,12.1存在20618595bug(具体可参见官方文档) Oracle 12c 中安装oracle application expr...

youfen
40分钟前
0
0
OpenMessaging概览

序 本文主要研究一下OpenMessaging 架构图 namespace,类似cgroup的namespace,用来进行安全隔离,每个namespace有自己的producer、consumer、topic、queue等 producer,消息生产者有两类,一...

go4it
45分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部