这是 Xamarin Forms 中的内存泄漏吗? [英] Is this a memory leak in Xamarin Forms?

查看:16
本文介绍了这是 Xamarin Forms 中的内存泄漏吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我遇到了一个问题,页面对象在导航离开后似乎没有被垃圾收集.我已经整理了一个非常基本的示例,它演示了使用 NavigationPage 和 PushAsync 方法时的问题.该页面使用弱引用列表显示活动"页面的数量:

I have run into a problem where it appears Page objects are not being Garbage Collected once they have been navigated away from. I have put together a very basic example of this that demonstrates the issue when using a NavigationPage and the PushAsync method. The page displays the number of 'Alive' pages using a list of weak references:

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 处理).但是,当我运行此测试代码时,它表明在我们返回后,此视图将保留在内存中.这可以通过单击 Next Page 几次直到引用计数为 3 来证明.如果您然后单击 Back 然后 Next Page,我相信引用计数应该仍然是 3(表明旧页面在新页面之前被 GC 处理)创建了一个)但是新的引用计数现在是 4.

As you click the Next Page button, a new page is created with a fixed value label showing the number of page references alive at the point this page was created. Each time you click the button you obviously see this number increase by 1. My understanding is that when you click 'back' on the Navigation Page, the view should be popped off the stack and thrown away (allowing it to be GC'd). However, when I run this test code it indicates that after we have gone back, this view is being retained in memory. This can be demonstrated by clicking Next Page a few times until the reference count is at 3. If you then click Back and then Next Page, I believe the reference count should still be 3 (indicating the old page was GC'd before the new one was created) however the new reference count is now 4.

这似乎是 iOS 的 X-Forms 导航实现中的一个相当严重的错误(我还没有在其他平台上测试过),我的猜测是它与此处描述的强引用循环问题有某种关系:http://developer.xamarin.com/guides/cross-platform/application_fundamentals/memory_perf_best_practices/

This seems like quite a serious bug in the X-Forms navigation implementation for iOS (I haven't tested this for other platforms), my guess being it is somehow related to the Strong Reference Cycle problem described here: http://developer.xamarin.com/guides/cross-platform/application_fundamentals/memory_perf_best_practices/

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

Has anyone else encountered this and/or come up with a solution/workaround for it? Would anyone else agree this is a bug?

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

As an addition, I did a second example that doesn't involve a NavigationPage (so has to use PushModalAsync instead) and found I had the same problem, so this issue doesn't look to be unique to NavigationPage navigation. For reference the code for that (very similar) test is here:

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;
    }
}

推荐答案

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

I think what you are seeing is a side effect of async navigation, not memory leak. Instead of WeakReferences you might opt for a finalizer instead and create instances of MyPage (instead of 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() 调用,例如:

Next trick is to add a delayed GC.Collect() call, like:

    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.但那是另外一回事了.

You will note that instances get garbage collected within this delayed collection (output window). As per Xamarin GarbageCollector: I doubt that it has serious flaws. A minor bug here and there but not that huge. That said, dealing with garbage collections in Android is particularly tricky because there are two of those - Dalvik's and Xamarin's. But that is another story.

这篇关于这是 Xamarin Forms 中的内存泄漏吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆