【问题标题】:MonoTouch: App killed for low mem, Why? Live bytes allocation 5 MB topMonoTouch:应用程序因内存不足而被杀,为什么?实时字节分配 5 MB 顶部
【发布时间】:2012-05-09 13:11:14
【问题描述】:

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

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

这里是 heapshots 序列:

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

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

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

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

    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 报告活动字节很低,泄漏几乎为零,那么为什么不处理死对象呢?即使我的应用程序几分钟内什么都没发生,GC 似乎也没有完成他的工作,即使我在我的代码中明确地称之为它。我什至尝试了新的实验性垃圾收集器,但让我的应用程序死机更加糟糕和快捷。

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

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

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

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

【问题讨论】:

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


    【解决方案1】:

    您需要在图像创建代码周围使用 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 (释放时池会自动排空)。

    【讨论】:

    • 是和不是。很多时候 AutoreleasePools 有效,但这次不行。我试过了,但它不起作用。唯一阻止分配增长的是取消对次要踏板的使用。然后内存分配稳定不变,应用不再崩溃。使用自动释放池根本不会改变分配的速度或数量。我认为这是 MonoTouch GC 的错误,或者是关于在辅助线程中处理 UIKit 类的限制。
    • @EmanueleSabetta:如果您认为这是 MonoTouch GC 中的错误(或者是 MonoTouch 内部的问题),您应该在测试用例中提交错误 (bugzilla.xamarin.com)。
    • 现在很好用!!!!在后台任务中使用 NSAutoreleasePool 和 GC.Collect 对我有用。谢谢!!!!
    • 哈维尔:你能发布你的工作代码吗?我尝试使用池,但没有成功。也许你在代码中做了一些其他的事情来使这个技巧有效。
    • 好吧,我不得不说新的 xamarin monotuch 库运行良好,不需要执行 GC.Collect()。事实上,如果你调用 Collect(),系统会挂起一点,这使得播放音频/视频变得可怕。所以,我已经删除了 GC.Collect() 并且我完全没有内存压力!!
    【解决方案2】:

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

    我在后台任务中添加了GC.Collect,但并没有解决问题。我不得不删除后台任务,在主线程中完成所有工作并调用GC.Collect。似乎 Image.Dispose 没有释放图像内存:(

    但是当你有其他任务时它不起作用,所以我不得不删除它:(

    是的,它不工作....

    【讨论】:

    • 你是对的!我已经删除了后台任务并直接进行了调用,并且内存泄漏消失了!当 UIImages 在不同的线程中分配时,MonoTouch 垃圾收集器似乎有一个错误。但是现在当我滚动时我的应用程序没有响应,所以我需要找到一个解决方案来在不同的线程中执行它!但是怎么做呢?
    • 好吧,你可以在bugzilla.xamarin.com 中填写一个错误。我的英语不是很好,恐怕他们听不懂我... :(
    • 我相信他们已经意识到了这个错误。我读过 MonoTouch 5.3 beta 对一些 UIKit 多线程问题进行了一些修复。让我们拭目以待,看看它是否能解决我们的问题。
    【解决方案3】:

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

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

    我会猜测在 ui 线程上创建的对象正在被 ui 线程外部的一个引用,并且会在那里查看,但我不确定。

    我期待看到这方面的更新,因为我确信我会遇到类似的问题!

    祝你好运, 抢

    【讨论】:

    • 是的,一切正常吗 MonoTouch GC 应该在范围结束时自动处理所有内容。就像所有方法都已经被包裹在窗帘后面的 AutoreleasePool 中。但不知何故,当图形和 UI 线程中发生多个线程时,所有关于垃圾收集的自动操作都会停止工作。您在不同线程中引用某些内容是正确的。 UIImage 和 UIImageView 是主线程中的数据字段。但是,为什么如果我在不同的线程中引用某些内容,当我处理(甚至明确地!)它时它不会被垃圾收集?
    猜你喜欢
    • 1970-01-01
    • 2013-08-05
    • 1970-01-01
    • 2013-04-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-15
    相关资源
    最近更新 更多