Android性能優化(Memory)

性能相關:UI卡頓 / ANR / 內存泄漏——>OOMhtml

內存泄漏的本質:較長生命週期對象持有較短生命週期的引用致使,較短引用無法釋放內存。linux

GcRoots:Garbage Collector 的對象, 收集非GC Roots的引用對象,一般的GC Root有哪些?android

www.jianshu.com/p/dcfe84c50…web

經過System Class Loader或者Boot Class Loader加載的class對象,經過自定義類加載器加載的class不必定是GC Root
處於激活狀態的線程
棧中的對象
JNI棧中的對象
JNI中的全局對象
正在被用於同步的各類鎖對象
JVM自身持有的對象,好比系統類加載器等。
複製代碼

一般這裏涉及的 靜態的對象,其它運行線程持有當前的引用。shell

LeakCanary原理watch一個即將要銷燬的對象:bash

  1. 棧(stack)
  2. 堆(heap)
  3. 方法區(method)

常見的內存泄漏:

  1. 單例持有context, 寫成 ApplicationContext
  2. 非靜態內部類建立靜態實例形成的內存泄漏(改爲靜態的內部類)
  3. Handler(TLS,handler生命週期跟Activity的生命週期不同) handler.postDelay延遲發送。緣由message 持有handler,handler持有Activity,將Handler設置爲靜態的(以弱引用的方式持有Activity)
  4. 線程的內存泄漏。AsyncTask,Thread+Runnable,以及Handler(將他們定義爲static, 調用AsyncTask的Cancel方法)
  5. Webview,hybird。webview加載網頁申請native內存加載頁面,(1.將webview放在單獨的Webview的進程裏; 2. 在Webview所在的Activity在onDestory的時候killProcess)

LeakCanary源碼:

內存泄漏會形成OOM的罪魁禍首 探究源碼,檢測Activity泄漏的機制,LeakCanary的原理網絡

Activity泄漏檢測原理

  1. 將Activity Destory以後將它放在一個WeakReference
  2. 將這個WeakReference放到引用隊列ReferenceQueue

####ReferenceQueue 軟引用/弱引用app

對象被GC回收,Java虛擬機會把它加入到ReferenceQueue中dom

關於ReferenceQueue: www.cnblogs.com/dreamroute/…異步

四種引用類型:

StrongReference

softReference(內存空間不夠時纔回收)

WeakReference()

virtualReference(虛引用)

RefWatcher

監控Activity的內存泄漏

LeakCanary.enableDisplayLeakActivity(控制彈框)

  1. 建立一個refwatcher,啓動一個ActivityRefWatcher監聽Activity的生命週期的狀況(新版本沒有排除系統Reference的引用)

    //Refwatcher類結構
    public final class RefWatcher {
    
      public static final RefWatcher DISABLED = new RefWatcherBuilder<>().build();
    
      private final WatchExecutor watchExecutor;//執行內存泄漏檢測用的
      private final DebuggerControl debuggerControl;//查詢是否在代碼調試中,調試的時候就不檢測
      private final GcTrigger gcTrigger;//處理GC的,用於判斷泄漏以前給最後一次機會是否會GC,否者會顯示出來
      private final HeapDumper heapDumper;//Dump出內存泄漏的堆文件
      private final HeapDump.Listener heapdumpListener;//分析產生Heap文件的回調
      private final HeapDump.Builder heapDumpBuilder;
      private final Set<String> retainedKeys;//待檢測的產生泄漏的Key
      private final ReferenceQueue<Object> queue;//引用隊列,判斷弱引用持有的對象是否執行了GC回收
      ......
      }
    
    public void watch(Object watchedReference, String referenceName) {
        if (this == DISABLED) {
          return;
        }
        checkNotNull(watchedReference, "watchedReference");
        checkNotNull(referenceName, "referenceName");
        final long watchStartNanoTime = System.nanoTime();
      	//返回一個Key值,惟一的
        String key = UUID.randomUUID().toString();
      	//加入key到待檢測的隊列當中
        retainedKeys.add(key);
        final KeyedWeakReference reference =
            new KeyedWeakReference(watchedReference, key, referenceName, queue);
    	//開啓異步線程分析弱引用reference
        ensureGoneAsync(watchStartNanoTime, reference);
      }
    複製代碼

  2. 經過ActivityLifecycleCallbacks把Activity的ondestory生命週期關聯

    原來的ActivityRefWatcher被廢棄了

    /** * @deprecated This was initially part of the LeakCanary API, but should not be any more. * {@link AndroidRefWatcherBuilder#watchActivities} should be used instead. * We will make this class internal in the next major version. */
    @SuppressWarnings("DeprecatedIsStillUsed")
    @Deprecated
    public final class ActivityRefWatcher {}
    複製代碼

    換成 AndroidRefWatcherBuilder, 其實最終綁定 ActivityRefWatcher的生命週期

    // LeakCanary的入口
    public static @NonNull RefWatcher install(@NonNull Application application) {
        return refWatcher(application).listenerServiceClass(DisplayLeakService.class)
            .excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
            .buildAndInstall();
      }
    
    /** * Creates a {@link RefWatcher} instance and makes it available through {@link * LeakCanary#installedRefWatcher()}. * * Also starts watching activity references if {@link #watchActivities(boolean)} was set to true. * * @throws UnsupportedOperationException if called more than once per Android process. */
      public @NonNull RefWatcher buildAndInstall() {
        if (LeakCanaryInternals.installedRefWatcher != null) {
          throw new UnsupportedOperationException("buildAndInstall() should only be called once.");
        }
        RefWatcher refWatcher = build();
        if (refWatcher != DISABLED) {
          LeakCanaryInternals.setEnabledAsync(context, DisplayLeakActivity.class, true);
          if (watchActivities) {
            //這裏又調用原來廢棄的ActivityRefWatcher的方法
            ActivityRefWatcher.install(context, refWatcher);
          }
          if (watchFragments) {
            FragmentRefWatcher.Helper.install(context, refWatcher);
          }
        }
        LeakCanaryInternals.installedRefWatcher = refWatcher;
        return refWatcher;
      }
    
    //ActivityRefWatcher類下面的
     public static void install(@NonNull Context context, @NonNull RefWatcher refWatcher) {
        Application application = (Application) context.getApplicationContext();
       //建立activityRefWatcher
        ActivityRefWatcher activityRefWatcher = 
          new ActivityRefWatcher(application,refWatcher);
       //綁定生命週期
       application.registerActivityLifecycleCallbacks
         (activityRefWatcher.lifecycleCallbacks);
      }
    
      private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
          new ActivityLifecycleCallbacksAdapter() {
            @Override public void onActivityDestroyed(Activity activity) {
              //調用watch方法,監聽activity的泄漏
              refWatcher.watch(activity);
            }
          };
    複製代碼

  3. 最後在線程池中去開始分析咱們的泄漏

//開啓線程池分析
private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) {
    watchExecutor.execute(new Retryable() {
      @Override public Retryable.Result run() {
        return ensureGone(reference, watchStartNanoTime);
      }
    });
  }

//容錯性考慮
@SuppressWarnings("ReferenceEquality") // Explicitly checking for named null.
  Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) {
    long gcStartNanoTime = System.nanoTime();
    //從watch到GC的時間
    long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);
	//把已經回收的對象引用從 標記內存泄漏的retainedKeys中清除掉 
    removeWeaklyReachableReferences();

    if (debuggerControl.isDebuggerAttached()) {
      // The debugger can create false leaks.
      return RETRY;
    }
    if (gone(reference)) {
      return DONE;
    }
    //觸發GC後又會把回收的對象引用加入到Queue中。
    gcTrigger.runGc();
    removeWeaklyReachableReferences();
    if (!gone(reference)) {
      long startDumpHeap = System.nanoTime();
      long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime);

      File heapDumpFile = heapDumper.dumpHeap();
      if (heapDumpFile == RETRY_LATER) {
        // Could not dump the heap.
        return RETRY;
      }
      long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap);

      HeapDump heapDump = heapDumpBuilder.heapDumpFile(heapDumpFile).referenceKey(reference.key)
          .referenceName(reference.name)
          .watchDurationMs(watchDurationMs)
          .gcDurationMs(gcDurationMs)
          .heapDumpDurationMs(heapDumpDurationMs)
          .build();

      heapdumpListener.analyze(heapDump);
    }
    return DONE;
  }

//從retainedKeys中去除已經GC掉的對象
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;
    while ((ref = (KeyedWeakReference) queue.poll()) != null) {
      retainedKeys.remove(ref.key);
    }
  }
複製代碼

在ServiceHeapDumpListener (implements HeapDump.Listener)開啓真正的分析:

@Override public void analyze(@NonNull HeapDump heapDump) {
  checkNotNull(heapDump, "heapDump");
  HeapAnalyzerService.runAnalysis(context, heapDump, listenerServiceClass);
}
複製代碼
public final class HeapAnalyzerService extends ForegroundService implements AnalyzerProgressListener {

  private static final String LISTENER_CLASS_EXTRA = "listener_class_extra";
  private static final String HEAPDUMP_EXTRA = "heapdump_extra";

  public static void runAnalysis(Context context, HeapDump heapDump, Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
    setEnabledBlocking(context, HeapAnalyzerService.class, true);
    setEnabledBlocking(context, listenerServiceClass, true);
    Intent intent = new Intent(context, HeapAnalyzerService.class);
    intent.putExtra(LISTENER_CLASS_EXTRA, listenerServiceClass.getName());
    intent.putExtra(HEAPDUMP_EXTRA, heapDump);
    ContextCompat.startForegroundService(context, intent);
  }

  public HeapAnalyzerService() {
    super(HeapAnalyzerService.class.getSimpleName(), R.string.leak_canary_notification_analysing);
  }

  @Override protected void onHandleIntentInForeground(@Nullable Intent intent) {
    if (intent == null) {
      CanaryLog.d("HeapAnalyzerService received a null intent, ignoring.");
      return;
    }
    String listenerClassName = intent.getStringExtra(LISTENER_CLASS_EXTRA);
    HeapDump heapDump = (HeapDump) intent.getSerializableExtra(HEAPDUMP_EXTRA);
    
	//這裏去除調heapDump.excludedRefs對應的系統的
    HeapAnalyzer heapAnalyzer =
 new HeapAnalyzer(heapDump.excludedRefs, this, heapDump.reachabilityInspectorClasses);

    //進一步分析內存
    AnalysisResult result = heapAnalyzer.checkForLeak(heapDump.heapDumpFile, heapDump.referenceKey,
        heapDump.computeRetainedHeapSize);
    AbstractAnalysisResultService.sendResultToListener(this, listenerClassName, heapDump, result);
  }

}
複製代碼
  1. 將.hprof轉化成 SnapShot
  2. 優化GCRoots

checkForLeak

  1. 解析dump下文件的hprof,把dump文件parse成Snapshot文件
  2. 根據前面的弱引用定義的

findLeakTrace: 找到最短的路勁,找到內存泄漏大小。

/** * Searches the heap dump for a {@link KeyedWeakReference} instance with the corresponding key, * and then computes the shortest strong reference path from that instance to the GC roots. */
  public @NonNull AnalysisResult checkForLeak(@NonNull File heapDumpFile, @NonNull String referenceKey, boolean computeRetainedSize) {
    long analysisStartNanoTime = System.nanoTime();

    if (!heapDumpFile.exists()) {
      Exception exception = new IllegalArgumentException("File does not exist: " + heapDumpFile);
      return failure(exception, since(analysisStartNanoTime));
    }

    try {
      listener.onProgressUpdate(READING_HEAP_DUMP_FILE);
      //1. 將hprof文件轉化成Snapshot快照文件,包含了全部引用對象的路徑。
      HprofBuffer buffer = new MemoryMappedFileBuffer(heapDumpFile);
      HprofParser parser = new HprofParser(buffer);
      listener.onProgressUpdate(PARSING_HEAP_DUMP);
      Snapshot snapshot = parser.parse();
      listener.onProgressUpdate(DEDUPLICATING_GC_ROOTS);
      //2.刪除重複的GCRoots以及對象
      deduplicateGcRoots(snapshot);
      
      listener.onProgressUpdate(FINDING_LEAKING_REF);
      Instance leakingRef = findLeakingReference(referenceKey, snapshot);

      // False alarm, weak reference was cleared in between key check and heap dump.
      if (leakingRef == null) {
        return noLeak(since(analysisStartNanoTime));
      }
      return findLeakTrace(analysisStartNanoTime, snapshot, leakingRef, computeRetainedSize);
    } catch (Throwable e) {
      return failure(e, since(analysisStartNanoTime));
    }
  }
複製代碼
  1. 找到泄漏的路徑
private AnalysisResult findLeakTrace(long analysisStartNanoTime, Snapshot snapshot, Instance leakingRef, boolean computeRetainedSize) {

  listener.onProgressUpdate(FINDING_SHORTEST_PATH);
  ShortestPathFinder pathFinder = new ShortestPathFinder(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));
  }

  listener.onProgressUpdate(BUILDING_LEAK_TRACE);
  LeakTrace leakTrace = buildLeakTrace(result.leakingNode);

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

  long retainedSize;
  if (computeRetainedSize) {

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

    Instance leakingInstance = result.leakingNode.instance;

    retainedSize = leakingInstance.getTotalRetainedSize();

    // TODO: check O sources and see what happened to android.graphics.Bitmap.mBuffer
    if (SDK_INT <= N_MR1) {
      listener.onProgressUpdate(COMPUTING_BITMAP_SIZE);
      retainedSize += computeIgnoredBitmapRetainedSize(snapshot, leakingInstance);
    }
  } else {
    retainedSize = AnalysisResult.RETAINED_HEAP_SKIPPED;
  }

  return leakDetected(result.excludingKnownLeaks, className, leakTrace, retainedSize,
      since(analysisStartNanoTime));
}
複製代碼

補充:Application:單例模式

  1. 實例建立方式
  2. 全局實例
  3. 生命週期, 整個生命週期。

Application應用場景

  1. 初始化 全局對象、環境配置變量
  2. 獲取應用當前的內存狀況
  3. 監聽應用程序內 全部Activity的生命週期
  4. 內存監控 (onTrimMemory)TrimMemoryLevel、onLowMemory、onTerminate()、onConfigurationChanged
/** @hide */
//內存級別,對內存進行釋放。
@IntDef(prefix = { "TRIM_MEMORY_" }, value = {
  TRIM_MEMORY_COMPLETE,
    TRIM_MEMORY_MODERATE,
    TRIM_MEMORY_BACKGROUND,
    TRIM_MEMORY_UI_HIDDEN,
    //內存不足
    TRIM_MEMORY_RUNNING_CRITICAL,
    TRIM_MEMORY_RUNNING_LOW,
    TRIM_MEMORY_RUNNING_MODERATE,
})
複製代碼

onTrimMemory跟 onLowMemory都是內存優化的地方。

MAT以及Android Studio自己的內存監控: blog.csdn.net/u012760183/…

網絡流量和冷啓動

  1. 總體的性能解決思路
  2. 應用性能類型
  3. 各類性能數據指標
性能解決思路
  1. 監控性能指標,量化指標
  2. 根據上報統計信息
  3. 持續監控並觀察
應用性能種類
  1. 資源消耗
  2. 流暢度(網絡請求、UI繪製、冷啓動)
各個性能數據指標
  1. 網絡請求流量

    經過運營商的網絡訪問Internet。

    • 平常開發中能夠經過tcpdump + Wireshark抓包測試法

    • TrafficStats類:getMobileTxPackets, getMobileRxPackets, getMobileTxBytes, getMobileRxBytes

      (讀取linux文件系統的)

  2. 冷啓動

adb shell am start -W packagename /MainActivity

日誌打印:起點 ->終點

起點:Application的onCreate方法

終點:首頁ActivityOncreate加載完成

  1. UI卡頓Fps幀率

Fps:

Choreographer:經過日誌監控掉幀現象。

Vsync: 同步信號,硬件終端

流暢度:實際幀率/理論幀率

相關文章
相關標籤/搜索