简单活动导航的内存泄漏
Memory leak with simple activities navigation
我目前正在开发一个 Xamarin.Android 应用程序,其中包含很多活动,使用 API return 图像,有时还有大量对象列表,所以我有注意内存以避免OOM。
但是,当我开始使用位图时,我终于设法有了一些。起初,我认为是因为那些位图,所以我 运行 进行了一些测试。看起来它不是。我停用了 API,所以我使用的是空的对象列表,没有图像。而且我仍然设法让我的应用程序内存使用变得疯狂,最终崩溃。
由于我无法在 Visual Studio 上安装 Xamarin Profiler(不知道为什么,但不是这里的主要问题),我在我的设备(是 OnePlus One)上安装了一个名为 Intel 的应用程序Performance Viewer,能够实时监控设备的 RAM 使用情况。我启动了我的应用程序,假设我的 2GB 总内存中的内存使用量为 1.1GB。我单击了一个应该显示对象列表的按钮,但由于 API 已关闭,列表为空。因此,activity 包含一个自定义应用栏、两个按钮、一个空列表视图和一个显示 "list is empty" 的文本。调用此 activity,RAM 消耗从大约 30MB 上升。然后我按下我的 return 按钮,它正在调用 Finish();在 activity 上,然后我回到主要位置。 RAM 消耗从 1 MB 或 2 MB 左右下降。
然后,如果我再继续那个 activity,它就会开始,再吃 30 MB,如果我一直在这样的活动之间切换,它最终会导致 OOME。
我尝试在我的设备开发人员选项中检查 "Don't keep Activities",它似乎释放了更多 RAM(多么合乎逻辑),但仍然不是全部。在内存不足之前我必须切换更多,但问题仍然存在。
Finish 不应该将 activity 中包含的所有数据放入垃圾收集器吗?我试着做 GC.Collect();在我的 OnDestroy 中,没有效果。
我不知道我是否误解了内存在 android 上的工作原理,因为我对移动开发还很陌生,但这让我很头疼。有什么帮助吗?
谢谢!
编辑:这是 OOM 时我得到的结果。
05-23 10:15:37.973 D/dalvikvm( 5049): GC_FOR_ALLOC freed 1587K, 3% free 248825K/256263K, paused 22ms, total 23ms
05-23 10:15:37.973 I/dalvikvm-heap( 5049): Grow heap (frag case) to 245.840MB for 2908172-byte allocation
05-23 10:15:38.029 D/dalvikvm( 5049): GC_CONCURRENT freed 127K, 2% free 2515
37K/256263K, paused 10ms+10ms, total 52ms
05-23 10:15:38.077 D/dalvikvm( 5049): GC_FOR_ALLOC freed 74K, 2% free 251463K/256263K, paused 23ms, total 23ms
05-23 10:15:38.077 I/dalvikvm-heap( 5049): Forcing collection of SoftReferences for 11632652-byte allocation
05-23 10:15:38.105 D/dalvikvm( 5049): GC_BEFORE_OOM freed 306K, 2% free 251156K/256263K, paused 29ms, total 29ms
05-23 10:15:38.105 E/dalvikvm-heap( 5049): Out of memory on a 11632652-byte allocation.
05-23 10:15:38.105 I/dalvikvm( 5049): "main" prio=5 tid=1 RUNNABLE
05-23 10:15:38.105 I/dalvikvm( 5049): | group="main" sCount=0 dsCount=0 obj=0xa62704b0 self=0xb7c7b510
05-23 10:15:38.105 I/dalvikvm( 5049): | sysTid=5049 nice=0 sched=0/0 cgrp=[fopen-error:2] handle=-1217084352
05-23 10:15:38.105 I/dalvikvm( 5049): | schedstat=( 7886788399 5740305249 19539 ) utm=596 stm=192 core=0
05-23 10:15:38.117 E/mono-rt ( 5049): =================================================================
05-23 10:15:38.117 E/mono-rt ( 5049): Got a SIGSEGV while executing native code. This usually indicates
05-23 10:15:38.117 E/mono-rt ( 5049): a fatal error in the mono runtime or one of the native libraries
05-23 10:15:38.117 E/mono-rt ( 5049): used by your application.
05-23 10:15:38.117 E/mono-rt ( 5049): =================================================================
05-23 10:15:38.117 E/mono-rt ( 5049):
05-23 10:15:38.117 F/libc ( 5049): Fatal signal 11 (SIGSEGV) at 0x00000000 (code=1), thread 5049
我建议您阅读这篇关于以下内容的文章:
编辑:
尝试这样的事情:
using Android.Content;
using Java.IO;
using System;
namespace SampleTest.Droid
{
public class CacheManager
{
private static long MaxSize = 5242880L; // 5MB
private CacheManager() { }
public static void CacheData(Context context, Byte[] data, String name)
{
try
{
File cacheDir = context.CacheDir;
long size = cacheDir.TotalSpace;
long newSize = data.Length + size;
if (newSize > MaxSize)
{
CleanDir(cacheDir, newSize - MaxSize);
}
File file = new File(cacheDir, name);
FileOutputStream OS = new FileOutputStream(file);
try
{
OS.Write(data);
}
catch (Exception ex)
{
OS.Flush();
OS.Close();
System.Console.WriteLine(ex.Message);
throw;
}
}
catch (IOException ex)
{
System.Console.WriteLine(ex.Message);
throw;
}
}
private static void CleanDir(File dir, long bytes)
{
long bytesDeleted = 0;
File[] files = dir.ListFiles();
foreach (File file in files)
{
bytesDeleted += file.Length();
file.Delete();
if (bytesDeleted >= bytes)
break;
}
}
public static byte[] RetrieveData(Context context, String name)
{
try
{
File cacheDir = context.CacheDir;
File file = new File(cacheDir, name);
if (!file.Exists())
{
// Data doesn't exist
return null;
}
byte[] data = new byte[(int)file.Length()];
FileInputStream fis = new FileInputStream(file);
try
{
fis.Read(data);
}
finally
{
fis.Close();
}
return data;
}
catch (Exception ex)
{
System.Console.WriteLine(ex.Message);
throw;
}
}
private static long DirSize(File Dir)
{
long size = 0;
File[] files = Dir.ListFiles();
foreach (File file in files)
{
if (file.IsFile)
size += file.Length();
}
return size;
}
}
}
参考:
用片段替换活动。活动很繁重,我猜你一直在创建新的导航活动。
在您知道事情很容易失控之前,您的应用程序将达到 OOM 阈值。保持单个 activity 并替换其中的片段。
这里有一个关于片段的教程https://guides.codepath.com/android/Creating-and-Using-Fragments
好的,好消息(有点)。问题不在我最初认为的地方。我把这个内存问题搁置了几个小时,继续研究应用程序的最后功能。在测试我的新代码时,在出现 OOM 之前,我无法在活动之间导航超过 3 次。我就像 "wait a second, I could switch for hours without error before"。所以我用 Git 找到了这个内存泄漏问题的第一个提交。它首先出现了照片上传功能。所以,我的内存泄漏可能是因为一些未回收的位图。在 Stack 和互联网上的其他地方可以找到很多位图内存错误,我应该能够弄清楚。感谢所有回复的人!
我目前正在开发一个 Xamarin.Android 应用程序,其中包含很多活动,使用 API return 图像,有时还有大量对象列表,所以我有注意内存以避免OOM。
但是,当我开始使用位图时,我终于设法有了一些。起初,我认为是因为那些位图,所以我 运行 进行了一些测试。看起来它不是。我停用了 API,所以我使用的是空的对象列表,没有图像。而且我仍然设法让我的应用程序内存使用变得疯狂,最终崩溃。
由于我无法在 Visual Studio 上安装 Xamarin Profiler(不知道为什么,但不是这里的主要问题),我在我的设备(是 OnePlus One)上安装了一个名为 Intel 的应用程序Performance Viewer,能够实时监控设备的 RAM 使用情况。我启动了我的应用程序,假设我的 2GB 总内存中的内存使用量为 1.1GB。我单击了一个应该显示对象列表的按钮,但由于 API 已关闭,列表为空。因此,activity 包含一个自定义应用栏、两个按钮、一个空列表视图和一个显示 "list is empty" 的文本。调用此 activity,RAM 消耗从大约 30MB 上升。然后我按下我的 return 按钮,它正在调用 Finish();在 activity 上,然后我回到主要位置。 RAM 消耗从 1 MB 或 2 MB 左右下降。
然后,如果我再继续那个 activity,它就会开始,再吃 30 MB,如果我一直在这样的活动之间切换,它最终会导致 OOME。
我尝试在我的设备开发人员选项中检查 "Don't keep Activities",它似乎释放了更多 RAM(多么合乎逻辑),但仍然不是全部。在内存不足之前我必须切换更多,但问题仍然存在。
Finish 不应该将 activity 中包含的所有数据放入垃圾收集器吗?我试着做 GC.Collect();在我的 OnDestroy 中,没有效果。
我不知道我是否误解了内存在 android 上的工作原理,因为我对移动开发还很陌生,但这让我很头疼。有什么帮助吗?
谢谢!
编辑:这是 OOM 时我得到的结果。
05-23 10:15:37.973 D/dalvikvm( 5049): GC_FOR_ALLOC freed 1587K, 3% free 248825K/256263K, paused 22ms, total 23ms
05-23 10:15:37.973 I/dalvikvm-heap( 5049): Grow heap (frag case) to 245.840MB for 2908172-byte allocation
05-23 10:15:38.029 D/dalvikvm( 5049): GC_CONCURRENT freed 127K, 2% free 2515
37K/256263K, paused 10ms+10ms, total 52ms
05-23 10:15:38.077 D/dalvikvm( 5049): GC_FOR_ALLOC freed 74K, 2% free 251463K/256263K, paused 23ms, total 23ms
05-23 10:15:38.077 I/dalvikvm-heap( 5049): Forcing collection of SoftReferences for 11632652-byte allocation
05-23 10:15:38.105 D/dalvikvm( 5049): GC_BEFORE_OOM freed 306K, 2% free 251156K/256263K, paused 29ms, total 29ms
05-23 10:15:38.105 E/dalvikvm-heap( 5049): Out of memory on a 11632652-byte allocation.
05-23 10:15:38.105 I/dalvikvm( 5049): "main" prio=5 tid=1 RUNNABLE
05-23 10:15:38.105 I/dalvikvm( 5049): | group="main" sCount=0 dsCount=0 obj=0xa62704b0 self=0xb7c7b510
05-23 10:15:38.105 I/dalvikvm( 5049): | sysTid=5049 nice=0 sched=0/0 cgrp=[fopen-error:2] handle=-1217084352
05-23 10:15:38.105 I/dalvikvm( 5049): | schedstat=( 7886788399 5740305249 19539 ) utm=596 stm=192 core=0
05-23 10:15:38.117 E/mono-rt ( 5049): =================================================================
05-23 10:15:38.117 E/mono-rt ( 5049): Got a SIGSEGV while executing native code. This usually indicates
05-23 10:15:38.117 E/mono-rt ( 5049): a fatal error in the mono runtime or one of the native libraries
05-23 10:15:38.117 E/mono-rt ( 5049): used by your application.
05-23 10:15:38.117 E/mono-rt ( 5049): =================================================================
05-23 10:15:38.117 E/mono-rt ( 5049):
05-23 10:15:38.117 F/libc ( 5049): Fatal signal 11 (SIGSEGV) at 0x00000000 (code=1), thread 5049
我建议您阅读这篇关于以下内容的文章:
编辑:
尝试这样的事情:
using Android.Content;
using Java.IO;
using System;
namespace SampleTest.Droid
{
public class CacheManager
{
private static long MaxSize = 5242880L; // 5MB
private CacheManager() { }
public static void CacheData(Context context, Byte[] data, String name)
{
try
{
File cacheDir = context.CacheDir;
long size = cacheDir.TotalSpace;
long newSize = data.Length + size;
if (newSize > MaxSize)
{
CleanDir(cacheDir, newSize - MaxSize);
}
File file = new File(cacheDir, name);
FileOutputStream OS = new FileOutputStream(file);
try
{
OS.Write(data);
}
catch (Exception ex)
{
OS.Flush();
OS.Close();
System.Console.WriteLine(ex.Message);
throw;
}
}
catch (IOException ex)
{
System.Console.WriteLine(ex.Message);
throw;
}
}
private static void CleanDir(File dir, long bytes)
{
long bytesDeleted = 0;
File[] files = dir.ListFiles();
foreach (File file in files)
{
bytesDeleted += file.Length();
file.Delete();
if (bytesDeleted >= bytes)
break;
}
}
public static byte[] RetrieveData(Context context, String name)
{
try
{
File cacheDir = context.CacheDir;
File file = new File(cacheDir, name);
if (!file.Exists())
{
// Data doesn't exist
return null;
}
byte[] data = new byte[(int)file.Length()];
FileInputStream fis = new FileInputStream(file);
try
{
fis.Read(data);
}
finally
{
fis.Close();
}
return data;
}
catch (Exception ex)
{
System.Console.WriteLine(ex.Message);
throw;
}
}
private static long DirSize(File Dir)
{
long size = 0;
File[] files = Dir.ListFiles();
foreach (File file in files)
{
if (file.IsFile)
size += file.Length();
}
return size;
}
}
}
参考:
用片段替换活动。活动很繁重,我猜你一直在创建新的导航活动。 在您知道事情很容易失控之前,您的应用程序将达到 OOM 阈值。保持单个 activity 并替换其中的片段。
这里有一个关于片段的教程https://guides.codepath.com/android/Creating-and-Using-Fragments
好的,好消息(有点)。问题不在我最初认为的地方。我把这个内存问题搁置了几个小时,继续研究应用程序的最后功能。在测试我的新代码时,在出现 OOM 之前,我无法在活动之间导航超过 3 次。我就像 "wait a second, I could switch for hours without error before"。所以我用 Git 找到了这个内存泄漏问题的第一个提交。它首先出现了照片上传功能。所以,我的内存泄漏可能是因为一些未回收的位图。在 Stack 和互联网上的其他地方可以找到很多位图内存错误,我应该能够弄清楚。感谢所有回复的人!