There is no memory leak in LayoutInflater. While this is never a
proof, it's something we never encountered in
any of the apps we wrote. Also, the Android team works very hard to
plug all the memory leaks we can find
and when we tell developers to use less memory it's when we're pretty
sure that the APIs that seem to be
causing the memory leak are not responsible.

For instance, in your case you are basically saying that if you
inflate views until you run out of memory, you
run out of memory. Every View created by the adapter is kept by the
ListView in a 'recycler'. That recycler is
used to provide the adapter with a convertView whenever possible. That
means you MUST reuse the
convertView.

If you need views of different types in the list, just use the
appropriate APIs: Adapter.getViewTypeCount() and
Adapter.getItemViewType(int position) (see
http://d.android.com/reference/android/widget/Adapter.html). If
you implement these methods correctly, ListView will manage several
recyclers and always give you back the
correct convertView.

On Wed, Apr 8, 2009 at 6:14 AM, Niek <[email protected]> wrote:
>
> 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 <[email protected]> wrote:
>> On Tue, Apr 7, 2009 at 2:03 PM, Ward Willats <[email protected]> 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
>> [email protected]
>>
>> 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.
>
> >
>



-- 
Romain Guy
Android framework engineer
[email protected]

Note: please don't send private questions to me, as I don't have time
to provide private support.  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 [email protected]
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/android-developers?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to