【问题标题】:Xamarin Android garbage collection algorithmXamarin Android 垃圾回收算法
【发布时间】:2016-08-21 16:57:09
【问题描述】:

我正在阅读由reducing referenced instances 撰写的关于帮助 GC 更好地执行的 Xamarin.Android 垃圾收集文档。

该部分开头说:

每当在 GC 期间扫描 Java.Lang.Object 类型或子类的实例时,也必须扫描该实例引用的整个对象图。对象图是“根实例”引用的对象实例的集合,加上根实例所引用的所有内容,递归地。

...我明白了。

然后它会显示一个继承自标准 Activity 类的自定义类。这个自定义活动类有一个字段,它是一个字符串列表,在构造函数中初始化为具有 10,000 个字符串。据说这很糟糕,因为在 GC 期间必须扫描所有 10,000 个实例的可达性。我也明白。

我不清楚的部分是推荐的修复:它说应该将List<string> 字段移动到另一个不继承自Java.Lang.Object 的类,然后应该引用该类的实例活动就像之前引用的列表一样。

我的问题:当实例总数仍然是 10,000 并且开头段落说由于过程是递归的而最终将被扫描时,将字段更深地推入对象图中如何帮助 GC?

作为旁注,我还在阅读 (here) 关于 Mono 在 Android 上使用的 SGen GC 和对象图遍历过程被描述为从 GC 根开始的广度优先。这解释了一个包含 10,000 个项目的列表将如何在检查每个项目时导致更长的 GC 暂停,但仍然没有解释将该列表移入图表的更深处有何帮助,因为 GC 最终会在它深入图表时对其进行扫描。

【问题讨论】:

  • 这里的想法是我们避免进行同行步行。否则,我们需要在这些对象上同时查找 managed 和 Java 关系。您通常可以通过移动到static 类和Activity 派生类之外来根您的对象,因此它独立于对等点。我会说大多数时候,这不是主要问题,您应该正常编码。如果您随后分析您的应用程序并注意到 GC 瓶颈,您现在就知道该做什么了。即将这些类与消耗它们的对等对象分开。简而言之:在需要之前不要担心
  • @JonDouglas 感谢您的回复。您能否澄清一下“同行同行”的含义以及如何决定这样做或避免这样做?总的来说,我认为我们真的可以使用一篇文章来解释如何在 Xamarin Android 中创建对象和进行 GC,因为它的“两个 VM 并排工作”架构......清楚地说明应该有助于我们理解“ do a peer walk" 并可能帮助我们避免在此Xamarin Evolve 谈话中讨论的问题。

标签: android xamarin mono garbage-collection xamarin.android


【解决方案1】:

我会尽力解释这一点,我远不是专家,所以任何想插话的人,请这样做。

当我们提到做一个peer walk 时,我们正在定位任何roots 并遍历实时参考图以查看哪些是可访问的,哪些是不可访问的:

根对象:

  • 静态字段/属性指向的对象
  • 每个托管线程的堆栈上的对象
  • 已传递到本机 API 的对象

基本上,您必须处理两个托管 GC。我们将它们称为 Xamarin GC 和 Android GC 以供参考。

Xamarin.Android 有 peer objects,用于引用 Android JVM 中已知的本机 Java 对象。他们实现了一个核心接口:

namespace Android.Runtime
{
    public interface IJavaObject : IDisposable
    {
        // JNI reference to the Java object it is wrapping. 
        // Also known as a pointer to the JVM object
        public IntPtr Handle { get; set; }
        ...
    }
}

只要我们有一个继承了IJavaObject 的对象,它就会通过上面的JNI 句柄保持一个强引用,以确保只要被管理的对象还活着,它就保持活跃。

这样想:

IJavaObject -> IntPtr Handle -> Java Object

用 GC 术语表示如下:

Allocated and collected by Xamarin GC -> GC Root -> Allocated and collected by Android GC

然后我们在 Xamarin.Android 中有一个 GC 进程:

当 GC 运行时,您可以看到它将用弱引用替换强 JNI 句柄,然后调用将收集我们的 Java 对象的 Android GC。因此,会扫描peers 中的任何关系以确保它们在JVM 中被镜像。这样可以防止这些对象被过早收集。

一旦发生这种情况,我们就会运行 Android GC,完成后它将遍历对等对象并检查弱引用。

  • 如果一个对象消失了,我们会在 C# 端收集它
  • 如果对象仍然存在,那么我们将弱引用改回强 JNI 句柄

因此,每次在peer 对象上运行 GC 时,都需要检查和更新此图。这就是为什么这些包装器类型对象要慢得多的原因,因为必须从对等对象开始扫描整个对象图。

所以当我们的peer 对象使用重要的对象图时,我们可以通过将引用的存储移到peer 类之外来帮助GC 过程。这通常由rooting 我们的参考独立于对等方来完成。而且由于它没有存储为字段,GC 不会尝试在对象图上进行关系遍历。

如前所述,在您注意到长 GC 之前,这不是一个需要担心的大问题。然后,您可以将其用作解决方案。

图片来源:Xamarin 大学(https://www.xamarin.com/university)

【讨论】:

  • 感谢图表!再澄清一点:在流程图中,“是对等对象吗?”检查...它是否只是查看对象是否实现了IJavaObject 对象接口,或者是否有另一种方式可以将对象视为对等对象?
  • 是的,这是正确的。 developer.xamarin.com/guides/android/advanced_topics/…有关该项目的更多信息。
猜你喜欢
  • 1970-01-01
  • 2011-12-21
  • 2012-01-28
  • 2013-06-27
  • 2011-11-29
  • 2021-12-20
  • 2011-07-15
  • 2014-03-09
  • 2013-06-10
相关资源
最近更新 更多