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

查看:77
本文介绍了这是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). .但是,当我运行此测试代码时,它表明我们返回后,该视图仍保留在内存中.这可以通过多次单击下一页"直到引用计数为3来演示.如果您依次单击上一步"和下一页",则我相信引用计数仍应为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天全站免登陆