我已经读到某个Android应用程序的内存限制为16MB(某些设备可能更高)。所以当从网络加载一些数据(如图像)时,它看起来是正确的。
在我的虚拟设备(适用于Android的Visual Studio模拟器)上,内存限制大约为46MB。我想这是因为我在抛出OutOfMemoryException之前可以加载的图像数是46个图像(每个图像的大小约为1MB)。
起初我以为只有在ImageViews上显示图像时才会超出内存(它们包含在ListView或RecyclerView的项目中)。但是,无论是否显示加载的图像,仍会抛出异常(我可以在输出窗口中看到它)。
所以我想重现OutOfMemoryException,例如加载某种类型的本地列表,例如List<int>
,项目数约为13 000 000(应该超过46 MB)。但是不会抛出异常。我甚至尝试将物品数量增加到130,000 000并且仍然相同,不会抛出任何异常。这是代码:
//the local list is declared inside my main Activity class
List<int> _ints = new List<int>();
for(var i = 0; i < 130000000; i++) {
_ints.Add(i);
}
所以看起来我以错误的方式理解OutOfMemoryException的原因。您能否向我解释一下这个问题,或者如果可能的话,给我一些示例代码或建议来重现OutOfMemoryException,但不涉及从网络下载任何内容,我的意思是尽可能保持最简单。
你得到的OutOfMemoryException
来自Java方面。您不能仅使用C#对象来重现它。
您需要分配大型Java对象。它不适用于许多小对象,因为可以从C#代码引用多少Java对象是有限制的。
Android.Graphics.Bitmap
是一个很好的候选人:
List<Bitmap> _ints = new List<Bitmap>();
for (var i = 0; i < 130000000; i++)
{
_ints.Add(Bitmap.CreateBitmap(1000, 1000, Bitmap.Config.Argb8888));
}
由此产生的Java异常(包含在单声道异常中):
Java.Lang.OutOfMemoryError: Failed to allocate a 4000012 byte allocation with 219956 free bytes and 214KB until OOM
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x0000c] in <368820a9888f43ddb85d18e87189adbf>:0
at Java.Interop.JniEnvironment+StaticMethods.CallStaticObjectMethod (Java.Interop.JniObjectReference type, Java.Interop.JniMethodInfomethod, Java.Interop.JniArgumentValue* args) [0x00082] in <a043032cf94a485190047a14918b9f60>:0
at Java.Interop.JniPeerMembers+JniStaticMethods.InvokeObjectMethod (System.String encodedMember, Java.Interop.JniArgumentValue* parameters) [0x00019] in <a043032cf94a485190047a14918b9f60>:0
at Android.Graphics.Bitmap.CreateBitmap (System.Int32 width, System.Int32 height, Android.Graphics.Bitmap+Config config) [0x0005e] in <3140893b79904cefbb68181cd89998e0>:0
at SomeActivity.OnCreate (Android.OS.Bundle savedInstanceState) [0x00024] in <efd0cb86bcd8422d9a46a0a8a1804b4d>:0
at Android.Support.V4.App.FragmentActivity.n_OnCreate_Landroid_os_Bundle_ (System.IntPtr jnienv, System.IntPtr native__this, System.IntPtr native_savedInstanceState) [0x00011] in <71c3e52f1b484794bca1cdfb1b8b1fdb>:0
at (wrapper dynamic-method) System.Object:46337ec2-c41a-4805-8728-4ab907d01a03 (intptr,intptr,intptr)
--- End of managed Java.Lang.OutOfMemoryError stack trace ---
java.lang.OutOfMemoryError: Failed to allocate a 4000012 byte allocation with 219956 free bytes and 214KB until OOM
at dalvik.system.VMRuntime.newNonMovableArray(Native Method)
at android.graphics.Bitmap.nativeCreate(Native Method)
at android.graphics.Bitmap.createBitmap(Bitmap.java:812)
at android.graphics.Bitmap.createBitmap(Bitmap.java:789)
at android.graphics.Bitmap.createBitmap(Bitmap.java:756)
at md53b9888b0c3fe89a24cb980ab2ea2a8a2.SomeActivity.n_onCreate(Native Method)
at md53b9888b0c3fe89a24cb980ab2ea2a8a2.someActivity.onCreate(SomeActivity.java:32)
at android.app.Activity.performCreate(Activity.java:5990)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1106)
编辑:使用Xamarin,内存问题更复杂,因为有两个垃圾收集器,并且两个中都存在一些像位图这样的对象。
这在Xamarin documentation中有解释,特别是章Helping the GC
public void OOMTest() throws Exception {
int count = 20;
System.out.println("\n=================> started..\n");
for (int itrator = 1; itrator < 100; itrator++) {
System.out.println("Iteration " + itrator + " Free Mem: "
+ Runtime.getRuntime().freeMemory());
int[] memoryFillIntVar = new int[count];
count = count * 5;
System.out.println("\nRequired Memory for next loop: " + count);
Thread.sleep(1000);
}
}
关于应用程序的堆大小,它因手机而异。它是从制造商设置的,最小通常是16MB。您可以通过在Android Manifest中标记它来强制在您的应用中使用更大的堆,但这应该作为最后的手段,因为它会导致系统杀死其他应用程序,以获得可用内存。
在您的情况下,请注意,即使图像未显示,如果另一个对象持有对它的引用,它仍可能仍在应用程序内存中。 Android中的图像加载和管理由于堆的内存限制通常会出现问题。为了克服这个问题,Facebook编写了自己的图像加载库Fresco,它使用不同的堆加载内存,避免使用应用程序中其他对象使用的空间。如果你处理很多图像,请考虑在你的应用程序中介绍它。