c# - MonoTouch : App killed for low mem, 为什么?事件字节分配 5 MB 顶部

标签 c# memory-management memory-leaks xamarin.ios memory-warning

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

所以我启动了 Instruments 并开始分析我的应用程序在 iPad 上的内存分配。在那里我发现事件字节只有 5-9 MB 左右,这正是我所期望的,但是由于一些奇怪的原因,死内存分配几乎根本没有被收集,因为在分配了大约 50 MB(少于 5-9 MB 的内存)之后它是 Live Bytes)它正在被杀死!这是我的应用程序的仪器分析 session 的屏幕截图:

Instruments screenshots documenting memory allocation problem under monotouch

这是heapshots序列:

heapshots of my app in instruments

还有一些小漏洞,但我认为它们还不足以成为罪魁祸首。它们都是 strdup 泄漏的 48 字节,这是在 iOS 5.1 中发布 UIScrollView 时的一个已知问题:

enter image description here

即使看起来一切正常并且分配的 Live Bytes 仍然是 5 MB,我的应用程序的真实内存在被杀死之前呈指数增长,在 iPad 上高达 50MB,在 iPhone4S 上高达 314 MB,据报道内存监视器:

memory growth of my App on iPhone4S

有人可以告诉我是否有一种方法或实用程序可以发现问题出在哪里?这是单点垃圾收集器的错误吗?还是有一些我没有正确处理的物体?我如何使用分析器找到那些?我已经检查了我的代码两天,但似乎一切正常。

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

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

是否有一种方法可以捕获未正确处置的对象? Instruments 报告 live bytes 很少,泄漏几乎为零,那么为什么不处理 dead objects?即使我的应用程序在几分钟内没有任何反应,GC 似乎也没有完成他的工作,即使我在我的代码中明确地调用它。我什至尝试了新的实验性垃圾收集器,但它让我的应用程序被杀死的速度更快。

我在 Xamarin 网站上找不到任何用于解决内存警告或跟踪错误分配的指南或分步指南。

非常感谢任何帮助或建议, 谢谢!

更新: 我已经减小了一些图像和 uiview 的大小和分辨率,Live Bytes 现在从 9 MB 降到 5 MB top,但无论如何,应用程序仍然会在一些内存警告后被杀死。

更新 2: 正如 Javier 所建议的那样,我删除了后台任务并直接进行了调用,内存泄漏消失了!似乎 MonoTouch 垃圾收集器有一个错误,当 UIImages 在不同的线程中分配和处置时无法收集内存。但是现在我的应用程序在我滚动时完全没有响应,所以我需要找到一个解决方案来在不同的线程中完成它!但是如何呢?

最佳答案

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

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(该池将在处理时自动排空)。

关于c# - MonoTouch : App killed for low mem, 为什么?事件字节分配 5 MB 顶部,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10373464/

相关文章:

memory-management - Linux内核中的事件/非事件列表

c++ - 最大化内存使用

c# - 合并两个字典的键

c# - 为什么使用 dll 而不是类

objective-c - cocoa /ObjC : Are there any performance drawbacks to my style of allocing objects for property iVars?

c++ - 如何检查我的 C++ 程序在内存中的增长状态?

c++ - Visual Studio 中的内存泄漏

c++ - 销毁时c++容器使用的巧妙内存概述

c# - 数据库到 DropDownList 和 AutoPostBack 到标签

c# - .NET 正则表达式 - 较短的匹配