In my app, I have a ViewSwitcher with two children like this:
<ViewFlipper
android:id="#+id/galeryviewflipper"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ImageView
android:id="#+id/galeryimageview_0"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:scaleType="fitCenter"
/>
(... other stuff here)
</RelativeLayout>
<RelativeLayout>
(... copy previous except for IDs)
</RelativeLayout>
</ViewFlipper>
Then I'm switching gallery images (each is a photo about 800x600):
//(setup wanted images into ImageViews)
//then:
viewFlipper.setInAnimation(instance, R.anim.fade_in);
viewFlipper.setOutAnimation(instance, R.anim.fade_in);
viewFlipper.setDisplayedChild(nextIndex);
On android 2.3.4 everything goes as expected, but on 2.3.5 phone (galaxy S2), dalvik calls several times GC during animation, which results in a sloppy user experience (notice GC times and duration!). I don't call System.gc anyhere in app explicitly.
05-18 21:06:56.040: D/dalvikvm(6617): GC_EXTERNAL_ALLOC freed <1K, 50% free 5447K/10695K, external 17083K/17815K, paused 30ms
05-18 21:06:56.115: D/dalvikvm(6617): GC_EXTERNAL_ALLOC freed <1K, 50% free 5447K/10695K, external 17083K/17815K, paused 30ms
05-18 21:06:56.175: D/dalvikvm(6617): GC_EXTERNAL_ALLOC freed <1K, 50% free 5447K/10695K, external 17083K/17815K, paused 31ms
05-18 21:06:56.245: D/dalvikvm(6617): GC_EXTERNAL_ALLOC freed <1K, 50% free 5447K/10695K, external 17083K/17815K, paused 30ms
05-18 21:06:56.305: D/dalvikvm(6617): GC_EXTERNAL_ALLOC freed <1K, 50% free 5447K/10695K, external 17083K/17815K, paused 31ms
05-18 21:06:56.365: D/dalvikvm(6617): GC_EXTERNAL_ALLOC freed <1K, 50% free 5447K/10695K, external 17083K/17815K, paused 30ms
05-18 21:06:56.415: D/dalvikvm(6617): GC_EXTERNAL_ALLOC freed <1K, 50% free 5447K/10695K, external 17083K/17815K, paused 32ms
It also does the same thing on another 2.2+ phone (not a galaxy s2), so in total on one phone no GC calls, on two other ones this happens. And, the OK phone is much less powerful than a Galaxy S2, and still displays a smooth fade in-out due to absence of GC calls during the animation. Any suggestions? Thanks for help.
It is definitely not caused by insufficient memory, on Galaxy S2 5MB is used out of 64MB heap during this process. It seems to be unrelated to the actual heap usage, it happens every time.
Related
https://developer.android.com/tools/debugging/debugging-memory.html refers to GC_FOR_MALLOC, which I do not see in my log. It does not refer to GC_FOR_ALLOC at all.
I do see GC_FOR_ALLOC:
03-12 10:11:02.382: D/dalvikvm(21382): GC_FOR_ALLOC freed 5383K, 30% free 27403K/39060K, paused 48ms, total 49ms
03-12 10:11:20.912: D/dalvikvm(21382): GC_FOR_ALLOC freed 8100K, 30% free 27392K/39060K, paused 63ms, total 63ms
What is the difference between these two GC events?
GC_FOR_ALLOC is just the new name that is used for GC_FOR_MALLOC on newer versions of Android, so they're the same. Google should really update that documentation :/
I got the answer from here in case you want to check it out: What do GC_FOR_MALLOC, GC_EXPLICIT, and other GC_* mean in Android Logcat?
This is a general question.
In my application the garbage collector is running too often (several times per second) and doesn't free a lot of memory (less than 1Mb).
The thing is, the heap size doesn't grow anymore. It takes about 40Mb, sometimes 60Mb on other phones where the maximum for an application is 128Mb (Galaxy S4) or 196Mb (Nexus 4).
Why does the GC run so often ?
Below is a part of the logcat when the GC runs too often. The application is freezing. I don't even do anything, I just try to zoom a map.
06-07 16:50:52.003: D/dalvikvm(26176): GC_FOR_ALLOC freed 256K, 18% free 60015K/72980K, paused 51ms, total 51ms
06-07 16:50:52.053: D/dalvikvm(26176): GC_FOR_ALLOC freed 256K, 18% free 60016K/72980K, paused 51ms, total 51ms
06-07 16:50:52.113: D/dalvikvm(26176): GC_FOR_ALLOC freed 256K, 18% free 60016K/72980K, paused 52ms, total 52ms
06-07 16:50:52.163: D/dalvikvm(26176): GC_FOR_ALLOC freed 256K, 18% free 60017K/72980K, paused 52ms, total 52ms
06-07 16:50:52.213: D/dalvikvm(26176): GC_FOR_ALLOC freed 256K, 18% free 60017K/72980K, paused 51ms, total 52ms
06-07 16:50:52.273: D/dalvikvm(26176): GC_FOR_ALLOC freed 256K, 18% free 60018K/72980K, paused 51ms, total 51ms
06-07 16:50:52.324: D/dalvikvm(26176): GC_FOR_ALLOC freed 256K, 18% free 60018K/72980K, paused 51ms, total 51ms
06-07 16:50:52.374: D/dalvikvm(26176): GC_FOR_ALLOC freed 256K, 18% free 60019K/72980K, paused 52ms, total 52ms
06-07 16:50:52.434: D/dalvikvm(26176): GC_FOR_ALLOC freed 256K, 18% free 60019K/72980K, paused 53ms, total 53ms
06-07 16:50:52.484: D/dalvikvm(26176): GC_FOR_ALLOC freed 256K, 18% free 60020K/72980K, paused 52ms, total 52ms
06-07 16:50:52.544: D/dalvikvm(26176): GC_FOR_ALLOC freed 256K, 18% free 60020K/72980K, paused 56ms, total 56ms
06-07 16:50:52.594: D/dalvikvm(26176): GC_FOR_ALLOC freed 256K, 18% free 60021K/72980K, paused 52ms, total 52ms
06-07 16:50:52.654: D/dalvikvm(26176): GC_FOR_ALLOC freed 256K, 18% free 60021K/72980K, paused 52ms, total 52ms
06-07 16:50:52.704: D/dalvikvm(26176): GC_FOR_ALLOC freed 256K, 18% free 60022K/72980K, paused 52ms, total 52ms
06-07 16:50:52.754: D/dalvikvm(26176): GC_FOR_ALLOC freed 256K, 18% free 60022K/72980K, paused 52ms, total 52ms
06-07 16:50:52.814: D/dalvikvm(26176): GC_FOR_ALLOC freed 256K, 18% free 60023K/72980K, paused 52ms, total 52ms
06-07 16:50:52.864: D/dalvikvm(26176): GC_FOR_ALLOC freed 256K, 18% free 6023K/72980K, paused 51ms, total 51ms
06-07 16:50:52.924: D/dalvikvm(26176): GC_FOR_ALLOC freed 256K, 18% free 60024K/72980K, paused 55ms, total 56ms
06-07 16:50:52.974: D/dalvikvm(26176): GC_FOR_ALLOC freed 256K, 18% free 60024K/72980K, paused 52ms, total 52ms
06-07 16:50:53.024: D/dalvikvm(26176): GC_FOR_ALLOC freed 256K, 18% free 60025K/72980K, paused 52ms, total 52ms
Your problem is related to how Google Maps Android API works and how much memory is allocated by your application.
40-60 MB might problematic because it takes longer to execute single GCollection. As a side note: I noticed adding 1000 markers takes significantly longer when having 15 MB allocated comapred to when having 5 MB.
In addition to that every call to the Google Play Services APIs is doing IPC, which in turn forces GC according to this answer. It is possible that such IPC calls are done when panning or zooming.
It would be preferable to keep your memory footprint much lower.
The GC runs when it needs to. In general, (even on the DVM) it really does know, way better than you do, when it needs to run. If you frame this problem not as a problem with the GC, but as a problem with your app's memory, it might help you find a solution.
The most likely reason for frequent GCs is that you are nearly out of memory. This could happen, for instance, if you allocate lots of short-lived objects: You allocate them fast, but forget them just as quickly. Every time the GC runs, it successfully recovers enough memory for the app to continue... but just as quickly, you fill memory again.
If this is actually what is happening, then, if you had more memory, as suggested by #parry, you would just delay the problem. GCs would occur less frequently, but take longer to run (because they would collect more garbage). That might be sufficient, but probably not.
Check to see if you are allocating objects in a routine that is called frequently (for instance, your draw routines).
If you believe the GC is acting stupid (quite possible), and that the VM has more memory to spare, then you could try forcing it to increase the heap size by asking for a one off 1/5/10 MB allocation (guarding it with a try-catch for OOME).
This might cause the GC to clean up more on each cycle, or increase the chances for a full GC to occur.
Another heuristic, which works on a PC, is calling System.GC() x times in a loop to trigger a full GC.
All in all, I came across it myself and it sucks.
I'm loading an image resource in an Android application using setImageResource(), and for some reason this is using a lot of extra memory.
This is what I see when the image is loaded:
03-29 15:16:56.687: D/dalvikvm(23616): GC_FOR_ALLOC freed 42K, 11% free 16175K/18119K, paused 11ms
I/dalvikvm-heap(23616): Grow heap (frag case) to 23.154MB for 7675216-byte allocation
D/dalvikvm(23616): GC_CONCURRENT freed 3K, 8% free 23667K/25671K, paused 1ms+2ms
D/dalvikvm(23616): GC_FOR_ALLOC freed 0K, 8% free 23667K/25671K, paused 10ms
I/dalvikvm-heap(23616): Grow heap (frag case) to 39.624MB for 17272816-byte allocation
D/dalvikvm(23616): GC_CONCURRENT freed 0K, 5% free 40535K/42567K, paused 1ms+2ms
The jpg is 1599x1200, so I would expect the first allocation - 1599 * 1200 * 4 = 7675200.
What's going on with the 17MB allocation?
It is most likely you put your image into drawable-mdpi or just drawable directory, but run the app on the hdpi device. In this case image's dimensions will be scaled by 1.5.
1599 * 1200 * 4 * 1.5 * 1.5 = 17269200b ~ 17mb
You should probably move the image to drawable-nodpi directory to avoid unwanted scale.
I've got a relatively light weight app that seems to be using large portions of heap memory (in my opinion) and it doesn't shrink after garbage collection.
I haven't been able to identify any memory leaks by using the Eclipse Memory Analyzer. My knowledge of this tool is though very limited.
Snippet from LogCat:
(Note that this is only a small snippet from a log dump. The LogCat seems to output garbage collection messages almost no matter what I do inside my app, continously. The amount of free heap remains relatively stable though, indicating (to me) that there is no actual memory leak?)
01-22 17:04:51.672: D/dalvikvm(16274): GC_CONCURRENT freed 721K, 10% free 8074K/8968K, paused 4ms+7ms, total 33ms
01-22 17:04:53.742: D/dalvikvm(16274): GC_CONCURRENT freed 523K, 12% free 7977K/8968K, paused 4ms+5ms, total 29ms
01-22 17:04:54.012: D/dalvikvm(16274): GC_CONCURRENT freed 457K, 12% free 7941K/8968K, paused 3ms+2ms, total 29ms
01-22 17:04:56.432: D/dalvikvm(16274): GC_CONCURRENT freed 237K, 10% free 8116K/8968K, paused 2ms+2ms, total 22ms
01-22 17:04:58.632: D/dalvikvm(16274): GC_CONCURRENT freed 445K, 10% free 8094K/8968K, paused 3ms+3ms, total 33ms
01-22 17:05:00.332: D/dalvikvm(16274): GC_CONCURRENT freed 499K, 11% free 8013K/8968K, paused 1ms+10ms, total 33ms
01-22 17:05:00.582: D/dalvikvm(16274): GC_CONCURRENT freed 487K, 12% free 7916K/8968K, paused 3ms+6ms, total 38ms
01-22 17:05:02.382: D/dalvikvm(16274): GC_CONCURRENT freed 223K, 10% free 8107K/8968K, paused 3ms+3ms, total 23ms
01-22 17:05:03.882: D/dalvikvm(16274): GC_CONCURRENT freed 436K, 10% free 8107K/8968K, paused 9ms+12ms, total 76ms
01-22 17:05:05.392: D/dalvikvm(16274): GC_CONCURRENT freed 528K, 11% free 8059K/8968K, paused 2ms+3ms, total 35ms
01-22 17:05:06.962: D/dalvikvm(16274): GC_CONCURRENT freed 489K, 11% free 7998K/8968K, paused 4ms+3ms, total 32ms
01-22 17:05:07.212: D/dalvikvm(16274): GC_CONCURRENT freed 487K, 12% free 7928K/8968K, paused 3ms+3ms, total 29ms
01-22 17:05:08.832: D/dalvikvm(16274): GC_CONCURRENT freed 226K, 10% free 8094K/8968K, paused 3ms+3ms, total 22ms
01-22 17:05:12.152: D/dalvikvm(16274): GC_CONCURRENT freed 453K, 10% free 8080K/8968K, paused 4ms+3ms, total 48ms
The above is a result of "heavy use", involving spamming on buttons and changing orientation several times. My concern is the fact that only ~10% of the heap seems to be free/left for further expansions.
For your information, the XML file representing the current fragment's layout (at the time of the above output) is a ScrollView with a TableLayout inside, consisting of about 25 TableRow elements, perhaps this is the cause for such large hogging of heap memory?
Is this something to worry about at all?
Please let me know if you prefer to look at some of my code. Thanks in advance.
Update:
The app is basically just one Activity containing two fragments. One of said fragments get swapped out with other fragments based on user interaction.
Think of it as a typical Menu-Content app (like the default Android Contacts app). MenuFragment (Contacts list) on the left and some ContentFragment (Contacts details) on the right. So far there is not too much functionality
involved except for setting up the UI behavior. There is nothing happening in the background, no saving of states or similar. I've basically focused on making sure that
the correct fragment shows up when I select an item from my MenuFragment, that the correct layouts are drawn when a fragment launches, and that the correct fragment
is displayed when the user presses the back button.
The heap will adjust it's size automatically based upon the needs of your app. Small heaps are faster on the GC so Android will not start you out with a 64 MB heap if you don't need it. While you're app is using a lot of your current heap, the heap is extremely small for an Android 4.x app. A bare bones 4.x app alone will use up 7Mb. From what I see, you're fine.
It's a similar question to this, but the solution doesn't work.
The problem is that the scrolling of ListView is very sluggish which is because of lots and lots of GCs. I use holder pattern (view caching) as you can see in the code below:
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.eventrow, parent, false);
holder = new ViewHolder();
holder.title = (TextView) convertView.findViewById(R.id.eventTitle);
holder.place = (TextView) convertView.findViewById(R.id.eventPlace);
convertView.setTag(holder);
} else {
// Get the ViewHolder back to get fast access to the TextView
// and the ImageView.
holder = (ViewHolder) convertView.getTag();
}
// Bind the data efficiently with the holder.
holder.title.setText(((EventItem) getItem(position)).getTitle());
holder.place.setText(((EventItem) getItem(position)).getPlace_name());
return convertView;
}
I removed cacheColorHint and custom selectors from my listview and from rows:
<ListView
android:id="#+id/android:list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/>
This is how the log looks like:
08-20 19:36:24.286: D/dalvikvm(12036): GC_EXTERNAL_ALLOC freed 364K, 46% free 3944K/7239K, external 1196K/1445K, paused 54ms
08-20 19:36:24.356: D/dalvikvm(12036): GC_EXTERNAL_ALLOC freed 228K, 49% free 3716K/7239K, external 1721K/1970K, paused 52ms
08-20 19:36:24.456: D/dalvikvm(12036): GC_EXTERNAL_ALLOC freed 5K, 49% free 3726K/7239K, external 2214K/2463K, paused 50ms
08-20 19:36:24.546: D/dalvikvm(12036): GC_EXTERNAL_ALLOC freed 2K, 49% free 3726K/7239K, external 2214K/2463K, paused 43ms
08-20 19:36:24.636: D/dalvikvm(12036): GC_EXTERNAL_ALLOC freed 2K, 49% free 3726K/7239K, external 2214K/2463K, paused 44ms
08-20 19:36:24.696: D/dalvikvm(12036): GC_EXTERNAL_ALLOC freed <1K, 49% free 3726K/7239K, external 2214K/2463K, paused 50ms
08-20 19:36:24.766: D/dalvikvm(12036): GC_EXTERNAL_ALLOC freed <1K, 49% free 3726K/7239K, external 2214K/2463K, paused 46ms
08-20 19:36:24.846: D/dalvikvm(12036): GC_EXTERNAL_ALLOC freed <1K, 49% free 3726K/7239K, external 2214K/2463K, paused 46ms
08-20 19:36:24.906: D/dalvikvm(12036): GC_EXTERNAL_ALLOC freed <1K, 49% free 3726K/7239K, external 2214K/2463K, paused 46ms
08-20 19:36:24.986: D/dalvikvm(12036): GC_EXTERNAL_ALLOC freed <1K, 49% free 3726K/7239K, external 2214K/2463K, paused 46ms
08-20 19:36:25.056: D/dalvikvm(12036): GC_EXTERNAL_ALLOC freed <1K, 49% free 3726K/7239K, external 2214K/2463K, paused 47ms
08-20 19:36:25.136: D/dalvikvm(12036): GC_EXTERNAL_ALLOC freed <1K, 49% free 3726K/7239K, external 2214K/2463K, paused 46ms
08-20 19:36:25.196: D/dalvikvm(12036): GC_EXTERNAL_ALLOC freed <1K, 49% free 3726K/7239K, external 2214K/2463K, paused 46ms
08-20 19:36:25.296: D/dalvikvm(12036): GC_EXTERNAL_ALLOC freed <1K, 49% free 3726K/7239K, external 2214K/2463K, paused 48ms
08-20 19:36:25.356: D/dalvikvm(12036): GC_EXTERNAL_ALLOC freed <1K, 49% free 3726K/7239K, external 2214K/2463K, paused 43ms
08-20 19:36:34.596: D/dalvikvm(12036): GC_EXTERNAL_ALLOC freed 7K, 49% free 3740K/7239K, external 2214K/2463K, paused 51ms
08-20 19:36:34.656: D/dalvikvm(12036): GC_EXTERNAL_ALLOC freed 1K, 49% free 3739K/7239K, external 2214K/2463K, paused 50ms
08-20 19:36:34.746: D/dalvikvm(12036): GC_EXTERNAL_ALLOC freed <1K, 49% free 3739K/7239K, external 2214K/2463K, paused 50ms
EDIT:
It seems that the problem appears (almost) only when the ListFragment is first inflated. For the first scroll down and then up. After that the GCs are not so often. But sometimes the GCs appear everytime I scroll. I can see no regularity here.
PLUS I can see the problem on HTC Desire (2.3.7 MIUI) but not on Sasmung Galaxy ACE (2.1). It only gets more confusing...
Not so satisfying solution:
When I use
android:scrollingCache="false"
android:animationCache="false"
in my listview it seems ok. But the docs say it should be exactly otherwise if I understand well, so I'm a little bit confused.
Where do I make mistake? What to do to have a smooth scrolling listview? Or would disabling scrollingCache have some negative effect which I can't see now?
android:scrollingCache="false"
Is the solution.
The docs say:
When set to true, the list uses a drawing cache during scrolling. This
makes the rendering faster but uses more memory.
Now this does not give us any hint about the implementation details. I have an idea without investing hours of code analyzes.
For caching purposes, like Google recommends in their own documents, they use WeakReferences or WeakHashMaps. But it looks like the Dalvik VM is a bit too eager to kill off these References and causes them to be rebuild every time. This explains the huge GC activity and the sluggish user experience.
Links:
Blog about useless WeakReferences due to bug: http://chriswstewart.com/post/14199645893/improving-the-android-listview
Dalvik VM bug discussion: https://groups.google.com/forum/#!topic/android-developers/66uw2t84rME
Make sure you have a background and/or a windowBackground for whatever is behind the ListView, I have seen this cause issues like this before.
try
listView.setCacheColorHint(0);
&
protected void onDestroy() {
System.gc();
super.onDestroy();
}