【问题标题】:Memory leaks: Garbage-Collection Root内存泄漏:垃圾收集根
【发布时间】:2018-05-10 19:30:42
【问题描述】:

在这种情况下,我试图了解一些与 Android 中的内存泄漏相关的概念。虽然这是一个常见问题,但我更感兴趣的是真正的理由,而不是像“不要保留对活动的引用”这样的简单答案。

这些是我目前为止一直在阅读的页面:

link1:垃圾收集根——所有对象树的来源

link2: Android 内存泄漏

link3: Android 内存泄漏

到目前为止,我的结论是,活动内存泄漏的唯一方法是当组件具有对活动的引用并且该组件的生命周期超出活动的生命周期时。发生内存泄漏的时间取决于该组件的生命周期。

话虽如此,我发现这个discussion 与 Jake Wharton 在 MVP 架构中与 Dagger 2 相关,他在其中写道:

没有泄漏。如果活动有传出引用 依赖项,并且没有对它的活动的传入引用 将被垃圾回收。

据我所知,如果我有一个活动引用了一个演示者,该演示者引用了一个视图接口(实现是相同的活动),然后该活动正在使用新的演示者重新创建,那么我看不到内存泄漏的原因:旧的活动和演示者不再可访问,因此它们可能被垃圾收集。如果演示者或演示者内的任何组件具有超出活动生命周期的生命周期,则我可能会发生内存泄漏的唯一方法。

我的问题是:

  • 我在这里遗漏了什么吗?

  • 引用活动是否意味着我会发生内存泄漏?如果是这样,那么这是否意味着该活动被视为垃圾收集根

【问题讨论】:

    标签: java android memory-leaks


    【解决方案1】:

    我花了一些时间回到这个问题并获取所有可能的信息以了解它是如何工作的,并查看语句“如果您有一个引用已被另一个活动替换的活动的对象,那么您有内存泄漏”是真的。

    首先,我决定运行一些代码来查看内存的行为。我使用了一个简单的 MVP 项目。 我做的第一件事是在调用 onDestroy 方法后在我的演示者中保留对活动的引用:

    public abstract class BasePresenter<T> {
    
        //Activity or Fragment attached to Presenter
        protected T view;
    
        public void onViewCreated(T view) {
            this.view = view;
        }
    
        public void onStart() {
    
        }
    
        public void onResume() {
    
        }
    
        public void onPause() {
    
        }
    
        public void onStop(){
    
        }
    
        public void onDestroyed() {
    //        view = null;
        }
    
    }
    

    然后我打开了“不在内存中保留活动”选项并重新创建了几次活动。

    当我转储内存时,我看到了该活动的多个实例,但在使用 android 内存分析器选项调用垃圾收集器后,它们消失了(正如您所知,由于性能问题,每次都不会调用 GC,只是在需要)。

    因此,在这种情况下,“如果您有一个对象引用的 Activity 已被另一个 Activity 替换,那么您就有内存泄漏”这句话是不正确的,或者至少它是不完整的。为了获得内存泄漏,该依赖图需要被 GC 根引用。

    然后我决定在 onDestroy 中取消注释该行:

      public void onDestroyed() {
        view = null;
      }
    

    并且在我的片段中有一个对视图的静态引用(视图对创建它们的活动有一个引用)

        private static View viewLeak;
    
        @Override
        public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
            super.onViewCreated(view, savedInstanceState);
    
            progressBar = view.findViewById(R.id.progress);
            if(viewLeak == null){
                viewLeak = progressBar;
            }
        }
    

    再次,我重复了重新创建活动的过程,在这种情况下,我有同一个活动的多个实例(两个实例:一个被积极使用,另一个被静态视图引用)。当然,我调用了垃圾收集器,但该活动的额外实例保留在内存中。这里的变化是,在这种情况下,我们有一个 GC 根,它是静态变量:

    静态变量由它们的类引用。这一事实使它们成为事实上的 GC 根源。 Source

    当然,在多种情况下您可以获得 CG 根,因此在多种情况下您可能会出现内存泄漏。

    我希望这可以帮助您澄清一些与 Java 中的内存泄漏相关的概念,就像它对我所做的那样。

    有助于理解这一点的其他来源是: LINK1, LINK2, LINK3

    新内容: 这是来自 google I/O 的 video 谈论内存泄漏。请注意,当他说“具有 Activity 引用的长寿命对象”时 - 分钟 25:30。

    【讨论】:

      【解决方案2】:

      假设您有一个对象A,它引用了活动B。现在假设您完成了活动B。您可以确定B 已准备好进行垃圾收集。

      现在假设您手动调用 GC。它会回收ActivityB吗? 不! 为什么?因为有一个对象引用它(对象A)。

      这就是内存泄漏的产生方式。

      【讨论】:

      • 嗨!我不认为这种说法是完全正确的。我添加了一个您可能感兴趣的答案。
      • 有一个有用的库叫做 LeakCanary。会检查你的解释,Lea!
      • 是的!我知道!我一直在使用它。很酷的图书馆,虽然我有时会收到一些误报。
      猜你喜欢
      • 2012-06-28
      • 1970-01-01
      • 1970-01-01
      • 2012-01-03
      • 2011-02-12
      • 2016-11-02
      • 2010-12-15
      • 2012-05-21
      • 1970-01-01
      相关资源
      最近更新 更多