我正在阅读有关通过reducing referenced instances帮助GC更好地发挥作用的Xamarin.Android垃圾收集文档。
该部分首先说:
每当在GC期间扫描Java.Lang.Object类型或子类的实例时,也必须扫描实例引用的整个对象图。对象图是“根实例”引用的对象实例集,以及根实例引用的所有引用的递归。
......我明白了。
然后显示继承自标准Activity类的自定义类。此自定义活动类有一个字段,该字段是在构造函数中初始化为具有10,000个字符串的字符串列表。据说这很糟糕,因为在GC期间必须扫描所有10,000个实例的可达性。我也理解。
我不清楚的部分是建议的修复:它说List<string>
字段应该移动到另一个不继承Java.Lang.Object
的类,然后是该类的实例应该从活动中引用,就像之前引用的列表一样。
我的问题:当实例总数仍为10,000时,如何将字段深入到对象图中有助于GC,并且开头的段落表示最终会扫描它们,因为该过程是递归的?
作为旁注,我也在Android上Mono使用的SGen GC上阅读(here),对象图遍历过程被描述为从GC根开始的广度优先。这解释了10,000项目列表如何在检查每个项目时导致更长的GC暂停,但仍然没有解释如何将该列表更深入地移动到图形中将有所帮助,因为GC会在深入到图形中时最终扫描它。
答案 0 :(得分:11)
我会尽力解释这一点,而且我离这里的专家不远,所以任何想要加入的人都请这样做。
当我们提到执行peer walk
时,我们会查找任何roots
并遍历实时参考图以查看可以访问的内容和不可访问的内容:
Root Objects:
基本上你必须处理两个管理的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
- &gt; IntPtr Handle
- &gt; Java Object
在GC术语中,它将表示如下:
Allocated and collected by Xamarin GC
- &gt; GC Root
- &gt; Allocated and collected by Android GC
然后我们在Xamarin.Android中有一个GC过程:
当GC运行时,您可以看到它将用弱引用替换强JNI句柄,然后调用将收集Java对象的Android GC。因此,会扫描peers
以查找任何关系,以确保它们在JVM中进行镜像。这样可以防止这些对象过早收集。
一旦发生这种情况,我们运行Android GC,当它完成时,它将遍历对等对象并检查弱引用。
因此,每次GC在peer
个对象上运行时,都需要检查和更新此图表。这就是为什么这些包装类型对象要慢得多,因为必须从对等对象开始扫描整个对象图。
因此,当我们的peer
对象使用重要的对象图时,我们可以通过移动peer
类之外的引用存储来帮助完成GC过程。这通常由rooting
我们的参考独立于同伴完成。由于它不是作为字段存储的,因此GC不会尝试在对象图上进行关系遍历。
如前所述,在您注意到长GC之前,这不是一个值得担心的问题。然后,您可以将其用作解决方案。
图片来源:Xamarin大学(https://www.xamarin.com/university)