首页 文章

这是Xamarin Forms中的内存泄漏吗?

提问于
浏览
19

我遇到了一个问题,一旦它们被导航出来,页面对象就不会被垃圾收集 . 我已经汇总了一个非常基本的示例,它演示了使用NavigationPage和PushAsync方法时的问题 . 该页面使用弱引用列表显示“活动”页面的数量:

public class AppNavigationPage
{
    private static List<WeakReference> pageRefs = new List<WeakReference>();

    public static Page GetMainPage()
    {
        return new NavigationPage(CreateWeakReferencedPage());
    }

    private static Page CreateWeakReferencedPage()
    {
        GC.Collect();
        var result = CreatePage();
        pageRefs.Add(new WeakReference(result));

        // Add a second unreferenced page to prove that the problem only exists
        // when pages are actually navigated to/from
        pageRefs.Add(new WeakReference(CreatePage()));
        GC.Collect();
        return result;
    }

    private static Page CreatePage()
    {
        var page = new ContentPage();
        var contents = new StackLayout();

        contents.Children.Add(
            new Button
            {
                Text = "Next Page",
                Command = new Command(() => page.Navigation.PushAsync(CreateWeakReferencedPage()))
            });
        contents.Children.Add(
            new Label
            {
                Text = string.Format(
                    "References alive at time of creation: {0}",
                    pageRefs.Count(p => p.IsAlive)),
                HorizontalOptions = LayoutOptions.CenterAndExpand
            });

        page.Content = contents;
        return page;
    }
}

单击“下一页”按钮时,将创建一个带有固定值标签的新页面,该标签显示在创建此页面时活动的页面引用数 . 每次单击按钮时,您都会看到此数字增加1.我的理解是,当您单击导航页面上的“返回”时,视图应从堆栈弹出并丢弃(允许它进行GC) . 但是,当我运行此测试代码时,它表示在我们返回之后,此视图将保留在内存中 . 这可以通过单击“下一页”几次直到引用计数为3来证明 . 如果然后单击“上一步”然后单击“下一页”,我相信引用计数仍应为3(表示旧页面在新页面之前已经过GC一个被创建了)但是新的引用计数现在是4 .

这似乎是iOS的X-Forms导航实现中的一个非常严重的错误(我没有在其他平台上测试过这个),我的猜测是它在某种程度上与这里描述的强参考周期问题有关:http://developer.xamarin.com/guides/cross-platform/application_fundamentals/memory_perf_best_practices/

有没有其他人遇到这个和/或为它提出解决方案/解决方法?有人会同意这是一个错误吗?

另外,我做了第二个不涉及NavigationPage的例子(因此必须使用PushModalAsync)并发现我遇到了同样的问题,所以这个问题看起来并不是NavigationPage导航的唯一问题 . 作为参考,该(非常相似)测试的代码在这里:

public class AppModal
{
    private static List<WeakReference> pageRefs = new List<WeakReference>();

    public static Page GetMainPage()
    {
        return CreateWeakReferencedPage();
    }

    private static Page CreateWeakReferencedPage()
    {
        GC.Collect();
        var result = CreatePage();
        pageRefs.Add(new WeakReference(result));

        // Add a second unreferenced page to prove that the problem only exists
        // when pages are actually navigated to/from
        pageRefs.Add(new WeakReference(CreatePage()));
        GC.Collect();
        return result;
    }

    private static Page CreatePage()
    {
        var page = new ContentPage();
        var contents = new StackLayout();

        contents.Children.Add(
            new Button
            {
                Text = "Next Page",
                Command = new Command(() => page.Navigation.PushModalAsync(CreateWeakReferencedPage()))
            });
        contents.Children.Add(
            new Button
            {
                Text = "Close",
                Command = new Command(() => page.Navigation.PopModalAsync())
            });
        contents.Children.Add(
            new Label
            {
                Text = string.Format(
                    "References alive at time of creation: {0}",
                    pageRefs.Count(p => p.IsAlive)),
                HorizontalOptions = LayoutOptions.CenterAndExpand
            });

        page.Content = contents;
        return page;
    }
}

1 回答

  • 2

    我认为你所看到的是异步导航的副作用,而不是内存泄漏 . 您可以选择使用终结器而不是WeakReferences,并创建MyPage(而不是ContentPage)的实例 .

    public class MyPage: ContentPage
        {
            private static int count;
    
            public MyPage()
            {
                count++;
                Debug.WriteLine("Created total " + count);
            }
            ~MyPage()
            {
                count--;
                Debug.WriteLine("Finalizer, remaining " + count);
            }
        }
    

    下一个技巧是添加延迟的GC.Collect()调用,如:

    private static Page CreateWeakReferencedPage()
        {
            GC.Collect();
            var result = CreatePage();
            var ignore = DelayedGCAsync();
            return result;
        }
    
        private static async Task DelayedGCAsync()
        {
            await Task.Delay(2000);
            GC.Collect();
        }
    

    您将注意到实例在此延迟集合(输出窗口)中收集垃圾 . 根据Xamarin GarbageCollector:我怀疑它有严重的缺陷 . 这里和那里的一个小错误但不是那么大 . 也就是说,处理Android中的垃圾收集特别棘手,因为其中有两个--Dalvik和Xamarin . 不过那是另一回事了 .

相关问题