简体   繁体   English

Android GC内存碎片失败。 解决方法?

[英]Android GC memory fragmentation fail. Workaround?

I am testing on android 3.1, large heapsize option, about 250M of memory available. 我正在测试android 3.1,大堆大小的选项,大约250M的内存可用。

I set the following code to be run whenever I hit a Test button in my app's prefs: 每当我点击应用程序首选项中的“测试”按钮时,我都会设置以下代码:

float [][][]foo = new float[3][2048][2048];
Bitmap bm = Bitmap.createBitmap(2048, 2048, Bitmap.Config.ARGB_8888);
bm.recycle();
bm  = null;
foo = null;

I have plenty of memory for this -- I can hit the button a few times without problem. 我有足够的记忆 - 我可以毫无问题地按几下按钮。

But if I keep hitting the button, eventually (less than 20 hits) it dies with OutOfMemory. 但是,如果我一直按下按钮,最终(少于20次点击)它会以OutOfMemory消失。 [Usually in android.graphics.Bitmap.nativeCreate(Native Method)] [通常在android.graphics.Bitmap.nativeCreate(Native Method)中]

Nothing else is going on -- I never have to leave the PreferencesActivity. 没有其他事情发生 - 我永远不必离开PreferencesActivity。 There is a small Toast that is also displayed when I hit the button, so a tiny amount of other UI activity is going on. 当我点击按钮时,还会显示一个小Toast,因此正在进行少量其他UI活动。

Is this due to fragmentation, or just a horrible bug in the android Bitmap code and/or GC? 这是由于碎片,还是Android Bitmap代码和/或GC中的一个可怕的错误? Or am I just doing something stupid? 或者我只是在做一些愚蠢的事情? (Please let it be something stupid...) (请让它成为愚蠢的......)

Does anybody have a workaround? 有人有解决方法吗? Because the above is fairly representative of what my code has to do each time the user invokes it, and right now despite meticulous clearing of variables it dies after a few uses. 因为上面的代码完全代表了我的代码每次用户调用它时所要做的事情,而且现在尽管细致地清除了变量,但它在几次使用后就会消失。 (And this has been driving me nuts for a long time now!) (这已经让我疯了很久了!)

[Update] [更新]

I have confirmed it's a fragmentation issue or gc bug, as a heap dump shows I'm only using 5.6M when idle (no leaks) peaking at about 26M during processing. 我已经确认这是一个碎片问题或gc错误,因为堆转储显示我在闲置(没有泄漏)时只使用5.6M处理期间大约26M达到峰值。 (Also, native heap stays below 4M.) While the java heap meanwhile grows in extent all the way to the 280M limit on my test device at which point I start getting OutOfMemory exceptions. (此外,本机堆保持在4M以下。)虽然java堆同时在我的测试设备上一直增长到280M限制,此时我开始获得OutOfMemory异常。 So I am only using 10% of my available heap at peak, but getting OutOfMemory. 所以我在峰值时只使用了10%的可用堆,但获得了OutOfMemory。

[Adding a call to System.gc() unfortunately fixes the simple test case I give above. [不幸的是,添加对System.gc()的调用修复了我上面给出的简单测试用例。 I say unfortunate because (A) it shouldn't make a difference, and (B) because I already call it regularly in my real code so it means my simple test case above is too simple.] 我说不幸,因为(A)它不应该有所作为,(B)因为我已经在我的实际代码中定期调用它,所以这意味着我上面的简单测试用例太简单了。

Has anyone else run into this? 有没有其他人遇到这个? Any workarounds? 任何解决方法? Is there a graceful way to restart my app? 有没有一种优雅的方式来重新启动我的应用程序?

[Update] [更新]

The following version reliably causes OutOfMemory in 3 to 4 invocations (presses of the button): 以下版本可以在3到4次调用中可靠地导致OutOfMemory(按下按钮):

float [][][]foo = new float[3][2048][2048];
Bitmap bm = Bitmap.createBitmap(2048, 2048, Bitmap.Config.ARGB_8888);
int []bar = new int[3*2048*2048];
bm.recycle();
bm = null;
System.gc();
foo = null;
System.gc();
bar = null;
System.gc();

Memory tracing shows the heap growing steadily each invocation until it hits the limit and dies. 内存跟踪显示堆在每次调用时稳定增长,直到达到限制并死亡。 If I remove any one of the three allocations, it reaches equilibrium and survives indefinitely. 如果我删除三个分配中的任何一个,它就会达到平衡并无限期地存活下来。 Removing all but the last gc() causes it to die slightly sooner. 删除除最后一个gc()之外的所有gc()会导致它稍早死亡。

I would say this is a fragmentation issue, not a gc bug per se. 我会说这是一个碎片问题,而不是gc bug本身。 If anybody knows how to fix it, let me know. 如果有人知道如何解决它,请告诉我。 The int[] allocation is for writing a Bitmap so I do not have the option of allocating it as a 2d array (limitation of the android Bitmap library). int []分配用于编写位图,因此我没有选择将其分配为2d数组(android Bitmap库的限制)。

Here is another SO page that apparently has a workaround for this problem: 这是另一个SO页面,显然有一个解决此问题的方法:

Strange out of memory issue while loading an image to a Bitmap object 将图像加载到Bitmap对象时出现奇怪的内存不足问题

Specifically, the answer by Ephraim (excerpted): 具体来说,以法莲的回答(摘录):

"1) Every time you do BitmapFactory.decodeXYZ(), make sure to pass in a BitmapFactory.Options with inPurgeable set to true (and preferably with inInputShareable also set to true). “1)每次执行BitmapFactory.decodeXYZ()时,请确保将inPurgeable设置为true的BitmapFactory.Options传入(并且最好将inInputShareable也设置为true)。

"2) NEVER use Bitmap.createBitmap(width, height, Config.ARGB_8888). I mean NEVER! I've never had that thing not raise memory error after few passes. No amount of recycle(), System.gc(), whatever helped. It always raised exception. The one other way that actually works is to have a dummy image in your drawables (or another Bitmap that you decoded using step 1 above), rescale that to whatever you want, then manipulate the resulting Bitmap (such as passing it on to a Canvas for more fun). So, what you should use instead is: Bitmap.createScaledBitmap(srcBitmap, width, height, false). If for whatever reason you MUST use the brute force create method, then at least pass Config.ARGB_4444." “2)永远不要使用Bitmap.createBitmap(width,height,Config.ARGB_8888)。我的意思是从来没有!我几乎没有通过几次没有引起内存错误。没有回收量(),System.gc(),它总是引发异常。另一种实际工作方式是在你的drawables中使用虚拟图像(或者你使用上面的步骤1解码的另一个Bitmap),将其重新缩放到你想要的任何位置,然后操纵生成的Bitmap(比如将它传递给Canvas以获得更多乐趣。所以,你应该使用的是:Bitmap.createScaledBitmap(srcBitmap,width,height,false)。如果由于某种原因你必须使用强力创建方法,那么在最少通过Config.ARGB_4444。“

In comments, some people said that this solved their problem, which is very similar to that of the OP here. 在评论中,有人说这解决了他们的问题,这与OP的问题非常相似。

I would add that Diane Hackborn has commented that as of 3.0 Android no longer allocates bitmaps from the native heap but instead directly allocates them from the regular heap. 我想补充一点,Diane Hackborn评论说,从3.0开始,Android不再从本机堆中分配位图,而是直接从常规堆中分配它们。 That may make your native heap figures irrelevant. 这可能会使您的本机堆数字无关紧要。 See hackbod's comment on this page: 请参阅hackbod对此页面的评论:

Bitmaps in Android Android中的位图

I guess that implies a fairly major change as of Honeycomb regarding bitmap allocation, and so that could explain why there are bugs with such allocations (if there are). 我想这意味着Honeycomb关于位图分配的一个相当大的变化,因此这可以解释为什么存在这种分配的错误(如果存在)。 I don't know what effect this change has on the recycle() command, but in light of the above comments by Ephraim the answer may be "not a very good one." 我不知道这个改变对recycle()命令有什么影响,但根据Ephraim的上述评论,答案可能是“不是很好的”。

Finally, 最后,

To use largeHeap to ingest huge bitmaps could be seen as not playing nice with other apps, especially if you are going close to the physical limits of the device. 使用largeHeap来摄取巨大的位图可能会被视为与其他应用程序不相称,特别是如果你接近设备的物理极限。 I'm not sure how you can avoid that, but be prepared for a lot of onPause() / onResume() activity as your app steps on other apps, and they step back on yours. 我不确定你怎么能避免这种情况,但是当你的应用程序在其他应用程序上执行时,请为许多onPause()/ onResume()活动做好准备,然后他们会退回到你的应用程序上。 This SO answer includes a discussion of this: 这个答案包括对此的讨论:

Detect application heap size in Android 在Android中检测应用程序堆大小

To prevent fragmentation, you could just allocate the large array AND the Bitmap once and reuse it. 为了防止碎片,你可以只分配大数组和Bitmap一次并重用它。

For Android, there is some caveats to this, as Android tries to manage your App's ressources to some degree. 对于Android,有一些警告,因为Android试图在某种程度上管理你的应用程序的资源。 For example, Activity 's or View 's may be unloaded if not visible, and re-run later if they became visible again. 例如,如果不可见,可以卸载ActivityView ,如果它们再次可见,则可以在以后重新运行。 So the large things should better be stored by an Application object or a static place. 因此,大型事物最好由Application对象或static位置存储。

If this is just used for some preference dialog, you should reserve it on the first use but keep it afterwards, to not use that much memory at every run. 如果这只是用于某些首选项对话框,您应该在第一次使用时保留它,但之后保留它,以便在每次运行时不使用那么多内存。 If it is used rarely, you maybe should restart your application after the preferences screen is left. 如果很少使用它,您可能应该在保留首选项屏幕后重新启动应用程序。 The user does not need to take notice of it if made well, and you would get a fresh and memory friendly process again. 如果做得好,用户不需要注意它,并且您将再次获得新的和记忆友好的过程。

There is a very detailed article about managing bitmap memory on android developer.android.com: 有一篇关于在an​​droid developer.android.com上管理位图内存的非常详细的文章:

http://developer.android.com/training/displaying-bitmaps/manage-memory.html http://developer.android.com/training/displaying-bitmaps/manage-memory.html

Basically they recommend to use Bitmap.recycle() in order to avoid OutOfMemoryError errors for Android 2.3.3 and lower. 基本上他们建议使用Bitmap.recycle()以避免Android 2.3.3及更低版本的OutOfMemoryError错误。 According to the documentation it frees the native object associated with the Bitmap Object. 根据文档,它释放与Bitmap对象关联的本机对象。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM