我的iPad应用程序是用MonoTouch开发的,因为我想避免所有的内存管理地狱,但情况似乎并非如此。在模拟器上一切正常,但当我在设备上测试我的应用程序时,我惊恐地发现,在一些内存警告之后,它很快就被操作系统杀死了。我的应用程序是一个简单的图像浏览器,它加载一些PNG图像,并在UIScrollView中使用一些UIViews显示它们,当获得触摸时加载下一个或前一个。在模拟器上,它工作正常。但在设备上,在加载和卸载大约6-11张图像后,它开始收到内存警告,然后突然该进程被终止。我已经检查了我的所有实例化周期,并且在加载新图像之前我正确地删除了所有引用。
因此,我启动了Instruments,并开始分析我的应用程序在iPad上的内存分配。在那里,我发现活动字节只有5-9MB,正如我所期望的,但由于某种奇怪的原因,死内存分配几乎根本没有被收集,因为在分配了大约50MB(少于5-9MB的活动字节)后,它被杀死了!以下是我的应用程序的Instruments分析会话的屏幕截图:

下面是heapshot序列:

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

即使看起来一切正常,分配的有效字节仍然是5MB,我的应用程序的实际内存在iPad上增长到50MB,在iPhone4S上增长到314MB,正如内存监视器所报告的那样:

有没有人能告诉我,有没有什么方法或实用程序可以发现问题所在?它是单点垃圾收集器的bug吗?还是有一些我没有正确处理的对象?我怎样才能用分析器找到它们呢?我已经检查我的代码两天了,但一切似乎都是正确的。
下面是我的加载/实例化/处置循环的代码:
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现在从9MB降到了5MB顶部,但无论发生什么内存警告,应用程序仍然会被杀死。
更新2:根据Javier的建议,我已经删除了后台任务,并直接调用,内存泄漏也就消失了!似乎MonoTouch垃圾收集器有一个错误,当UIImages被分配和释放在不同的线程中时,它无法收集内存。但现在我的应用程序在滚动时完全没有响应,所以我需要找到一个解决方案来在不同的线程中做这件事!但是怎么做呢?
发布于 2012-05-02 17:01:00
我和UIImage.FromFile有点问题。我的应用程序使用一个任务加载了很多png图像,并在主线程中显示它。
我在后台任务中添加了一个GC.Collect,但它并没有解决这个问题。我必须删除后台任务,完成主线程中的所有工作,并调用GC.Collect。Image.Dispose似乎没有释放图像内存:(
但是当你有另一个任务时它就不起作用了,所以我不得不删除它:(
是的,它不工作..。
发布于 2012-05-04 07:02:33
您需要在映像创建代码中使用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自动为所有用户线程创建自动释放池(包括线程池、第三方公共关系和普通System.Threading线程),但这些池直到线程退出时才会被排出(对于线程池和第三方公共关系线程,这是您无法控制的)。因此,解决方案是在关键代码周围使用NSAutoreleasePool (池将在释放时自动排空)。
发布于 2012-05-04 02:30:00
这对我来说很有趣,我刚刚开始研究内存管理,我甚至不确定是否必须使用null、object和dispose,因为作用域规则应该为您做这项工作?然而,这将有助于垃圾收集器尽快完成此操作。
显式请求垃圾收集器运行是一个请求,而不是保证。
我猜测在UI线程上创建的对象正被UI线程外部的一个对象引用,并在那里查找,但我不确定。
我期待着看到这方面的更新,因为我相信我也会遇到类似的问题!
祝你好运,Rob
https://stackoverflow.com/questions/10373464
复制相似问题