Xamarin Android 垃圾回收算法
Xamarin Android garbage collection algorithm
我正在阅读 Xamarin.Android 垃圾收集文档,了解如何通过 reducing referenced instances 帮助 GC 更好地执行。
该部分开头说:
Whenever an instance of a Java.Lang.Object type or subclass is scanned during the GC, the entire object graph that the instance refers to must also be scanned. The object graph is the set of object instances that the "root instance" refers to, plus everything referenced by what the root instance refers to, recursively.
...我明白了。
然后它会显示继承自标准 Activity class 的自定义 class。这个自定义 activity class 有一个字段,它是一个字符串列表,在构造函数中初始化为有 10,000 个字符串。据说这很糟糕,因为在 GC 期间必须扫描所有 10,000 个实例的可达性。这个我也明白。
我不清楚的部分是建议的修复:它说 List<string>
字段应该移动到另一个不继承自 Java.Lang.Object
的 class 并且那么 class 的实例应该从 activity 中引用,就像之前引用列表一样。
我的问题:当实例总数仍然是 10,000 并且开头段落说它们最终将被扫描,因为该过程是递归的,将字段更深入对象图中如何帮助 GC?
作为旁注,我也在阅读 (here) Mono 在 Android 上使用的 SGen GC,对象图遍历过程被描述为从 GC 根开始的广度优先。这解释了 10,000 项列表如何在检查每个项目时导致更长时间的 GC 暂停,但仍然没有解释将该列表移到图中更深的位置会有什么帮助,因为 GC 最终会在它深入到图中时对其进行扫描。
我会尽我所能解释这一点,我远不是这里的专家,所以任何想插话的人,请插话。
当我们提到做 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 运行s 时,您可以看到它将用弱引用替换强 JNI 句柄,然后调用 Android GC,它将收集我们的 Java 对象.因此,将扫描 peers
以查找任何关系以确保它们在 JVM 中被镜像。这可以防止这些对象被过早收集。
一旦发生这种情况,我们 运行 Android GC 完成后它将遍历对等对象并检查弱引用。
- 如果一个对象消失了,我们会在 C# 端收集它
- 如果对象仍然存在,那么我们将弱引用改回强 JNI 句柄
因此,每次 peer
对象上的 GC 运行 秒时,都需要检查和更新此图。这就是为什么这些包装器类型对象要慢得多的原因,因为必须从对等对象开始扫描整个对象图。
因此,当我们的 peer
对象使用大量对象图时,我们可以通过将引用存储移到 peer
class 之外来帮助 GC 过程。这通常由 rooting
我们独立于同行的参考来完成。由于它没有存储为字段,因此 GC 不会尝试在对象图上进行关系遍历。
如前所述,在您注意到长 GC 之前,这不是一个需要担心的大问题。然后您可以将其用作解决方案。
图片来源:Xamarin 大学(https://www.xamarin.com/university)
我正在阅读 Xamarin.Android 垃圾收集文档,了解如何通过 reducing referenced instances 帮助 GC 更好地执行。
该部分开头说:
Whenever an instance of a Java.Lang.Object type or subclass is scanned during the GC, the entire object graph that the instance refers to must also be scanned. The object graph is the set of object instances that the "root instance" refers to, plus everything referenced by what the root instance refers to, recursively.
...我明白了。
然后它会显示继承自标准 Activity class 的自定义 class。这个自定义 activity class 有一个字段,它是一个字符串列表,在构造函数中初始化为有 10,000 个字符串。据说这很糟糕,因为在 GC 期间必须扫描所有 10,000 个实例的可达性。这个我也明白。
我不清楚的部分是建议的修复:它说 List<string>
字段应该移动到另一个不继承自 Java.Lang.Object
的 class 并且那么 class 的实例应该从 activity 中引用,就像之前引用列表一样。
我的问题:当实例总数仍然是 10,000 并且开头段落说它们最终将被扫描,因为该过程是递归的,将字段更深入对象图中如何帮助 GC?
作为旁注,我也在阅读 (here) Mono 在 Android 上使用的 SGen GC,对象图遍历过程被描述为从 GC 根开始的广度优先。这解释了 10,000 项列表如何在检查每个项目时导致更长时间的 GC 暂停,但仍然没有解释将该列表移到图中更深的位置会有什么帮助,因为 GC 最终会在它深入到图中时对其进行扫描。
我会尽我所能解释这一点,我远不是这里的专家,所以任何想插话的人,请插话。
当我们提到做 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 运行s 时,您可以看到它将用弱引用替换强 JNI 句柄,然后调用 Android GC,它将收集我们的 Java 对象.因此,将扫描 peers
以查找任何关系以确保它们在 JVM 中被镜像。这可以防止这些对象被过早收集。
一旦发生这种情况,我们 运行 Android GC 完成后它将遍历对等对象并检查弱引用。
- 如果一个对象消失了,我们会在 C# 端收集它
- 如果对象仍然存在,那么我们将弱引用改回强 JNI 句柄
因此,每次 peer
对象上的 GC 运行 秒时,都需要检查和更新此图。这就是为什么这些包装器类型对象要慢得多的原因,因为必须从对等对象开始扫描整个对象图。
因此,当我们的 peer
对象使用大量对象图时,我们可以通过将引用存储移到 peer
class 之外来帮助 GC 过程。这通常由 rooting
我们独立于同行的参考来完成。由于它没有存储为字段,因此 GC 不会尝试在对象图上进行关系遍历。
如前所述,在您注意到长 GC 之前,这不是一个需要担心的大问题。然后您可以将其用作解决方案。
图片来源:Xamarin 大学(https://www.xamarin.com/university)