关于c#:MonoTouch:应用程序因内存不足而被杀,为什么?实时字节分配5 MB,顶部

MonoTouch: App killed for low mem, Why? Live bytes allocation 5 MB top

我的iPad应用程序是用MonoTouch开发的,因为我想避免所有的内存管理难题,但事实并非如此。在模拟器上,一切正常,但是当我在设备上测试我的应用程序时,我惊骇地发现,在出现一些内存警告后,它很快就被操作系统杀死了。
我的应用程序是一个简单的图像浏览器,它加载一些PNG图像,并使用UIScrollView内的一些UIViews显示它们,并在触摸时加载下一个或上一个。在模拟器上,它可以正常工作。但是在设备上加载和卸载大约6-11张图像后,它开始收到内存警告,然后突然终止了该过程。我已经检查了所有实例化周期,并在加载新图像之前正确删除了所有引用。

因此,我启动了Instruments,并开始在iPad上分析我的App的内存分配。在那里,我发现实时字节只有5-9 MB,恰好达到了我的预期,但是由于某些奇怪的原因,几乎没有收集死内存分配,因为分配了大约50 MB(小于5-9 MB的内存)后,它是Live Bytes)被杀死!这是我的App的"性能分析"会话的屏幕截图:

Instruments

有人可以告诉我是否有方法或实用程序来发现问题的所在和位置吗?它是monotouch垃圾收集器的错误吗?还是有一些我不正确处置的物体?以及如何使用探查器找到那些?我已经检查了我的代码两天,但是一切似乎都正确。

这是我的加载/实例化/处置周期的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
    void StartImageLoadingThread()
{
    tokenSource = new CancellationTokenSource ();
    token = tokenSource.Token;
    Task task1 = new Task( () => PerformLoadImageTask(token),token);
    task1.Start();
}


void PerformLoadImageTask(CancellationToken token)
{
        if (token.IsCancellationRequested)
                {
                     Console.WriteLine("Task {0}: Cancelling", Task.CurrentId);
                return;
                }

            current_uiimage_A = UIImage.FromFileUncached(file_name);
            page_A_image_view.Image = current_uiimage_A;


}


void UnloadImageAFromMemory()
{
      page_A_image_view.Image = null;
      current_uiimage_A.Dispose();
      current_uiimage_A = null;
}

是否有一种方法可以捕获未正确处置的对象?仪器报告说,活动字节很低,泄漏几乎为零,那么为什么不处理死对象呢?即使我的应用程序在几分钟之内什么都没发生,即使我在代码中明确地将其命名为GC,GC似乎也无法胜任。我什至尝试了新的实验性垃圾收集器,但是使我的应用程序被杀死甚至更糟,更快。

我无法在Xamarin网站上找到任何指南或分步指南来对内存警告进行故障排除或追踪不良分配。

非常感谢您的帮助或建议,
谢谢!

更新:
我已经减小了某些图像和uiview的大小和分辨率,并且"实时字节"现在从9 MB降到了5 MB,但是无论如何,在出现一些内存警告后,该应用程序仍然被杀死。

更新2:
正如哈维尔(Javier)的建议,我删除了后台任务,直接进行了调用,内存泄漏消失了!似乎MonoTouch垃圾收集器存在错误,并且在分配UIImages并将其分配到其他线程中时无法收集内存。但是现在当我滚动时,我的应用程序完全没有响应,因此我需要找到一个解决方案来在另一个线程中执行该操作!但是如何?


您需要在图像创建代码周围使用NSAutoreleasePools,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
void PerformLoadImageTask(CancellationToken token)
{
    if (token.IsCancellationRequested)
    {
         Console.WriteLine("Task {0}: Cancelling", Task.CurrentId);
         return;
    }

    using (var pool = new NSAutoreleasePool ()) {
        current_uiimage_A = UIImage.FromFileUncached(file_name);
        page_A_image_view.Image = current_uiimage_A;
    }
}

对于某些创建对象的API,该对象将被添加到自动释放池中,直到该池耗尽后才释放。 MonoTouch自动为所有用户线程(包括线程池,TPL和普通的System.Threading线程)创建自动释放池,但是直到线程退出(线程池和TPL线程无法控制)时,这些池才会耗尽)。因此,解决方案是在关键代码周围使用NSAutoreleasePool(在处置池时,池将自动被清空)。


我在UIImage.FromFile上遇到问题。我的应用程序正在使用任务加载许多png图像,并将其显示在主线程中。

我在后台任务中添加了GC.Collect,但是它没有解决问题。我必须删除后台任务,在主线程中执行所有操作,然后调用GC.Collect。似乎Image.Dispose没有释放图像内存:(

但是当您执行其他任务时它不起作用,所以我不得不将其删除:(

是的,它不起作用。...


这对我来说很有趣,我只是开始研究内存管理,我什至不确定是否对null和object和call dispose必不可少,因为范围界定规则应该为您完成工作?但是,它将帮助垃圾收集器尽快完成此操作。

明确要求运行垃圾收集器是一个请求,而不是保证。

我猜想在ui线程上创建的一个对象会在ui线程之外的一个中被引用,并且看起来在那里,但是我不

我希望看到此更新,因为我确定我会遇到类似的问题!

祝你好运,
罗伯