Android性能优化(Memory)

性能相关:UI卡顿 / ANR / 内存泄漏——>OOM

内存泄漏的本质:较长生命周期对象持有较短生命周期的引用导致,较短引用没法释放内存。

GcRoots:Garbage Collector 的对象, 收集非GC Roots的引用对象,通常的GC Root有哪些?

www.jianshu.com/p/dcfe84c50…

通过System Class Loader或者Boot Class Loader加载的class对象,通过自定义类加载器加载的class不一定是GC Root
处于激活状态的线程
栈中的对象
JNI栈中的对象
JNI中的全局对象
正在被用于同步的各种锁对象
JVM自身持有的对象,比如系统类加载器等。
复制代码

通常这里涉及的 静态的对象,其它运行线程持有当前的引用。

LeakCanary原理watch一个即将要销毁的对象:

  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 软引用/弱引用

对象被GC回收,Java虚拟机会把它加入到ReferenceQueue中

关于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 retainedKeys;//待检测的产生泄漏的Key
      private final ReferenceQueue 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. 最后在线程池中去开始分析我们的泄漏

  4. //开启线程池分析
    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 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: 同步信号,硬件终端

    流畅度:实际帧率/理论帧率

    你可能感兴趣的:(ui,shell,操作系统)