Hi guys,

I've read several posts from developers having OutOfMemory issues, and
a lot of them seem to be related to lists. I'm having a similar issue,
and the root of the problem seems to be a memory leak in the inflation
process.

The problem is particularly tricky to track down, because as of yet it
seems impossible to get a view of part of the heap (where the bitmaps
reside, and possibly other objects), as also mentioned by Ward and
others.

I created a small demo, so hopefully some Google engineers (Romain,
Dianne) can look at this, and either point out to me what I'm doing
wrong, or fix a possible memory leak in the inflation process in the
Android framework. I'm sure a lot of people would be very thankful if
this issue got resolved, as it often results in sudden crashes of the
app, without a clear reason. I must say it's disappointing to read the
standard: "use less memory" reply from Google to a lot of these posts,
as it seems to be worthy of further investigation.

Here's the demo, and the resulting logcat log with some peculiarities
contained within.

http://www.coffeebreakmedia.com/android_heap_issue.zip

A small explanation: it's a very simple list implementation with a
ListAdapter. It works fine if the list rows are inflated just once (by
re-using the convertView), but I purposely commented out two lines of
code:

//              if(convertView == null) {
                        convertView = inflater.inflate(R.layout.list_row, 
parent, false);
//              }

This is to ensure that for each row, the XML is inflated again. In
this demo, this would be completely unnecessary and a big waste, but
in a real life a similar scenario happens when you have a list with a
few different types of rows (e.g., a different xml is used for the
header of a set of items, which use their own xml). In that case,
sometimes the convertView will be of the wrong type, in which case you
have to inflate another one. Obviously, this would then happen less
often than in this demo, but this only postpones the issue: eventually
you'll run into the same problem as is demonstrated here.

To test the demo, simply open the project in Eclipse (you can use the
emulator, but it also happens on the G1), and follow these
instructions:

1. Completely scroll down the list as fast as you can (all the way to
Item 199), and then back up again.
2. Keep doing this until you scrolled down & up about 5 times, while
watching the LogCat.

Eventually, you'll start getting errors such as these:

04-08 12:15:03.380: ERROR/dalvikvm-heap(345): 13824-byte external
allocation too large for this process.
04-08 12:15:03.380: ERROR/(345): VM won't let us allocate 13824 bytes

Keep scrolling! And you'll see this after a few more ups & downs:

04-08 12:16:48.589: DEBUG/dalvikvm(345): GC freed 0 objects / 0 bytes
in 169ms
04-08 12:16:48.589: INFO/dalvikvm-heap(345): Clamp target GC heap from
16.003MB to 16.000MB
04-08 12:16:48.589: INFO/dalvikvm-heap(345): Grow heap (frag case) to
16.000MB for 24-byte allocation
04-08 12:16:48.810: INFO/dalvikvm-heap(345): Clamp target GC heap from
18.003MB to 16.000MB

Apparently, the 16MB heap is used completely at this point, which
seems rather wasteful considering the simplicity of the application in
question.

After still more scrolling, you'll get errors such as this:

04-08 12:17:01.700: ERROR/dalvikvm-heap(345): Out of memory on a 708-
byte allocation.
04-08 12:17:01.700: INFO/dalvikvm(345): "main" prio=5 tid=3 RUNNABLE
04-08 12:17:01.700: INFO/dalvikvm(345):   | group="main" sCount=0
dsCount=0 s=0 obj=0x400103e8
04-08 12:17:01.700: INFO/dalvikvm(345):   | sysTid=345 nice=0
sched=0/0 handle=-1093522276
04-08 12:17:01.700: INFO/dalvikvm(345):   at
java.lang.AbstractStringBuilder.enlargeBuffer
(AbstractStringBuilder.java:~100)
04-08 12:17:01.700: INFO/dalvikvm(345):   at
java.lang.AbstractStringBuilder.append0(AbstractStringBuilder.java:
140)
04-08 12:17:01.700: INFO/dalvikvm(345):   at
java.lang.StringBuffer.append(StringBuffer.java:257)
04-08 12:17:01.700: INFO/dalvikvm(345):   at java.io.StringWriter.write
(StringWriter.java:128)
04-08 12:17:01.700: INFO/dalvikvm(345):   at
java.io.PrintWriter.doWrite(PrintWriter.java:659)
04-08 12:17:01.700: INFO/dalvikvm(345):   at java.io.PrintWriter.write
(PrintWriter.java:640)
04-08 12:17:01.700: INFO/dalvikvm(345):   at java.io.PrintWriter.write
(PrintWriter.java:624)
04-08 12:17:01.700: INFO/dalvikvm(345):   at java.io.PrintWriter.write
(PrintWriter.java:677)
04-08 12:17:01.700: INFO/dalvikvm(345):   at java.io.PrintWriter.print
(PrintWriter.java:471)
04-08 12:17:01.700: INFO/dalvikvm(345):   at
java.io.PrintWriter.println(PrintWriter.java:589)
04-08 12:17:01.700: INFO/dalvikvm(345):   at
java.lang.Throwable.printStackTrace(Throwable.java:267)
04-08 12:17:01.700: INFO/dalvikvm(345):   at
android.util.Log.getStackTraceString(Log.java:234)
04-08 12:17:01.700: INFO/dalvikvm(345):   at
com.android.internal.os.RuntimeInit.crash(RuntimeInit.java:278)
04-08 12:17:01.700: INFO/dalvikvm(345):   at
com.android.internal.os.RuntimeInit$UncaughtHandler.uncaughtException
(RuntimeInit.java:75)
04-08 12:17:01.700: INFO/dalvikvm(345):   at
java.lang.ThreadGroup.uncaughtException(ThreadGroup.java:853)
04-08 12:17:01.700: INFO/dalvikvm(345):   at
java.lang.ThreadGroup.uncaughtException(ThreadGroup.java:850)
04-08 12:17:01.700: INFO/dalvikvm(345):   at
dalvik.system.NativeStart.main(Native Method)


and this:

04-08 12:18:04.530: ERROR/dalvikvm-heap(345): Out of memory on a 892-
byte allocation.
04-08 12:18:04.540: INFO/dalvikvm(345): "main" prio=5 tid=3 RUNNABLE
04-08 12:18:04.540: INFO/dalvikvm(345):   | group="main" sCount=0
dsCount=0 s=0 obj=0x400103e8
04-08 12:18:04.540: INFO/dalvikvm(345):   | sysTid=345 nice=0
sched=0/0 handle=-1093522276
04-08 12:18:04.540: INFO/dalvikvm(345):   at
java.lang.AbstractStringBuilder.enlargeBuffer
(AbstractStringBuilder.java:~100)
04-08 12:18:04.540: INFO/dalvikvm(345):   at
java.lang.AbstractStringBuilder.append0(AbstractStringBuilder.java:
140)
04-08 12:18:04.540: INFO/dalvikvm(345):   at
java.lang.StringBuffer.append(StringBuffer.java:257)
04-08 12:18:04.540: INFO/dalvikvm(345):   at java.io.StringWriter.write
(StringWriter.java:128)
04-08 12:18:04.540: INFO/dalvikvm(345):   at
java.io.PrintWriter.doWrite(PrintWriter.java:659)
04-08 12:18:04.540: INFO/dalvikvm(345):   at java.io.PrintWriter.write
(PrintWriter.java:640)
04-08 12:18:04.540: INFO/dalvikvm(345):   at java.io.PrintWriter.write
(PrintWriter.java:624)
04-08 12:18:04.540: INFO/dalvikvm(345):   at java.io.PrintWriter.write
(PrintWriter.java:677)
04-08 12:18:04.540: INFO/dalvikvm(345):   at java.io.PrintWriter.print
(PrintWriter.java:471)
04-08 12:18:04.540: INFO/dalvikvm(345):   at
java.io.PrintWriter.println(PrintWriter.java:589)
04-08 12:18:04.540: INFO/dalvikvm(345):   at
java.lang.Throwable.printStackTrace(Throwable.java:267)
04-08 12:18:04.540: INFO/dalvikvm(345):   at
android.util.Log.getStackTraceString(Log.java:234)
04-08 12:18:04.540: INFO/dalvikvm(345):   at
com.android.internal.os.RuntimeInit.crash(RuntimeInit.java:286)
04-08 12:18:04.540: INFO/dalvikvm(345):   at
com.android.internal.os.RuntimeInit$UncaughtHandler.uncaughtException
(RuntimeInit.java:75)
04-08 12:18:04.540: INFO/dalvikvm(345):   at
java.lang.ThreadGroup.uncaughtException(ThreadGroup.java:853)
04-08 12:18:04.540: INFO/dalvikvm(345):   at
java.lang.ThreadGroup.uncaughtException(ThreadGroup.java:850)
04-08 12:18:04.540: INFO/dalvikvm(345):   at
dalvik.system.NativeStart.main(Native Method)


Until eventually, the story ends rather sadly:

04-08 12:18:11.882: INFO/WindowManager(47): WIN DEATH: Window{43471f70
com.test/com.test.ListTestActivity}
04-08 12:18:11.882: INFO/ActivityManager(47): Process com.test (pid
345) has died.


And the app simply crashes. By the way, the same behavior will occur
if you remove the list icon. In a real app scenario, this also seems
to result in OutOfMemoryErrors such as these:

03-26 14:24:14.974: ERROR/dalvikvm-heap(381): 64000-byte external
allocation too large for this process.
03-26 14:24:14.984: ERROR/(381): VM won't let us allocate 64000 bytes
03-26 14:24:14.994: DEBUG/AndroidRuntime(381): Shutting down VM
03-26 14:24:14.994: WARN/dalvikvm(381): threadid=3: thread exiting
with uncaught exception (group=0x40013e28)
03-26 14:24:14.994: ERROR/AndroidRuntime(381): Uncaught handler:
thread main exiting due to uncaught exception
03-26 14:24:15.184: ERROR/AndroidRuntime(381):
java.lang.OutOfMemoryError: bitmap size exceeds VM budget
03-26 14:24:15.184: ERROR/AndroidRuntime(381):     at
android.graphics.Bitmap.nativeCreate(Native Method)
03-26 14:24:15.184: ERROR/AndroidRuntime(381):     at
android.graphics.Bitmap.createBitmap(Bitmap.java:343)
03-26 14:24:15.184: ERROR/AndroidRuntime(381):     at
android.view.View.buildDrawingCache(View.java:5219)
03-26 14:24:15.184: ERROR/AndroidRuntime(381):     at
android.view.View.getDrawingCache(View.java:5112)
03-26 14:24:15.184: ERROR/AndroidRuntime(381):     at
android.view.ViewGroup.drawChild(ViewGroup.java:1355)
03-26 14:24:15.184: ERROR/AndroidRuntime(381):     at
android.view.ViewGroup.dispatchDraw(ViewGroup.java:1192)
03-26 14:24:15.184: ERROR/AndroidRuntime(381):     at
android.widget.AbsListView.dispatchDraw(AbsListView.java:1125)
03-26 14:24:15.184: ERROR/AndroidRuntime(381):     at
android.widget.ListView.dispatchDraw(ListView.java:2778)
03-26 14:24:15.184: ERROR/AndroidRuntime(381):     at
android.view.View.draw(View.java:5422)
03-26 14:24:15.184: ERROR/AndroidRuntime(381):     at
android.view.ViewGroup.drawChild(ViewGroup.java:1420)
03-26 14:24:15.184: ERROR/AndroidRuntime(381):     at
android.view.ViewGroup.dispatchDraw(ViewGroup.java:1192)


Please check out the demo, and let us know your findings. One work-
around is to put all your different rows into one XML, and then use
GONE visibility to switch parts on and off depending on the row.
However, this may not fix the issue entirely, as you probably still
need to inflate again when you flip the screen and the Activity is
recreated.


Kind regards,
- Niek



On Apr 8, 12:39 am, Dianne Hackborn <hack...@android.com> wrote:
> On Tue, Apr 7, 2009 at 2:03 PM, Ward Willats <goo...@wardco.com> wrote:
> > (I mean, if I make a really small 4 bit indexed color png (no alpha)
> > does it get "blown up" to RGB_888 or ARGB in the view buffers before
> > being composited and handed to frame buffer memory?)
>
> They will generally either be loaded to 16bpp if there is no alpha channel,
> or 32bpp is there is an alpha channel.
>
> > And 9 patches...can I get their RAM consumption by taking their final
> > dimensions, and?
>
> Yes, 9-patches are basically just bitmaps.
>
> > If you rotate the screen, do two copies of the view hierarchy exist
> > for a moment?
>
> There is certainly a chance this can happen, though generally the first
> activity is destroyed before the second is created (but any lingering
> reference on any of the objects in the first activity can cause it to stay
> around until that ref goes away).  Those won't duplicate bitmaps, though,
> unless you are loading them yourself.
>
> --
> Dianne Hackborn
> Android framework engineer
> hack...@android.com
>
> Note: please don't send private questions to me, as I don't have time to
> provide private support, and so won't reply to such e-mails.  All such
> questions should be posted on public forums, where I and others can see and
> answer them.

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google
Groups "Android Developers" group.
To post to this group, send email to android-developers@googlegroups.com
To unsubscribe from this group, send email to
android-developers-unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/android-developers?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to