Related
I am a newbie in Android and now I am totally lost... could you give me some hints with my code?
I am trying to run a stop motion animation using AnimationDrawable from some of the images that I previously copied to the res folder of the project. The images to be used in the animation are selected in runtime, so I used addFrame to include them. When user push a button the animation starts successfully with the selected images. Then the user is asked for select new images and again, after pushing the button, the new animation should start. The problem is that then it causes OUT OF MEMORY errors. These errors do not happens in the first animation, so it seems that I am wasting the memory of the phone (or emulator) with the successive animations. How could I release the memory used in the first animation before starting the second one?
I tried using ((BitmapDrawable)frame).getBitmap().recycle(); but then I can not use the image for the second animation.
Any suggestions?
This is the relevant part of my code, I guess:
int globeId = this.getResources().getIdentifier(resname1, "drawable",
this.getPackageName());
animation.addFrame(this.getResources().getDrawable(globeId), velocidad);
int globeId = this.getResources().getIdentifier(resname2, "drawable",
this.getPackageName());
animation.addFrame(this.getResources().getDrawable(globeId), velocidad);
...
animation.setOneShot(true);
imageAnim.setBackgroundDrawable(animation);
animation.start();
And this is the error:
D/dalvikvm: GC_FOR_ALLOC freed 1251K, 5% free 59812K/62471K, paused 4ms, total 4ms
D/dalvikvm: GC_FOR_ALLOC freed <1K, 3% free 61062K/62471K, paused 4ms, total 4ms
I/dalvikvm-heap: Forcing collection of SoftReferences for 5119532-byte allocation
D/dalvikvm: GC_BEFORE_OOM freed 9K, 3% free 61052K/62471K, paused 4ms, total 6ms
E/dalvikvm-heap: Out of memory on a 5119532-byte allocation.
I have a problem with using the QNetworkAccessManager in Qt 5.5 on android. Downloading a simple, small graphic file via http GET results in a lot of garbage collection calls and a lockup of the UI during that time. Subsequent GETs work flawlessly and without these GC calls. The code is as follows:
void DownloadManager::downloadFile(QUrl fromUrl, QString toFilePath) {
_currentFilePath = toFilePath;
QNetworkRequest request;
request.setUrl(fromUrl);
qDebug() << "before";
_currentReply = _mgr.get(request);
qDebug() << "after";
connect(_currentReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(downloadError(QNetworkReply::NetworkError)));
connect(_currentReply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(downloadProgress(qint64,qint64)));
connect(_currentReply, SIGNAL(finished()), this, SLOT(downloadFinished()));
}
DownloadManager is a custom QObject-derived class without any special features that are relevant to the get request. _mgr is a QNetworkAccessManager Object that's allocated during DownloadManagers cTor.
As you can see, this is just a textbook example of a get request, nothing too fancy about it. And as I said: it works, for the most part. Only the first get request ends up like this:
D/ .../downloadmanager.cpp:61 (void DownloadManager::downloadFile(QUrl, QString)): before
D/dalvikvm(13298): GC_CONCURRENT freed 2290K, 25% free 10911K/14407K, paused 2ms+3ms, total 29ms
D/dalvikvm(13298): GC_CONCURRENT freed 501K, 25% free 10884K/14407K, paused 13ms+2ms, total 35ms
D/dalvikvm(13298): GC_CONCURRENT freed 524K, 25% free 10892K/14407K, paused 12ms+3ms, total 36ms
D/dalvikvm(13298): WAIT_FOR_CONCURRENT_GC blocked 6ms
D/dalvikvm(13298): GC_CONCURRENT freed 537K, 25% free 10887K/14407K, paused 2ms+9ms, total 32ms
D/dalvikvm(13298): WAIT_FOR_CONCURRENT_GC blocked 14ms
D/dalvikvm(13298): GC_CONCURRENT freed 840K, 25% free 10899K/14407K, paused 12ms+3ms, total 38ms
D/dalvikvm(13298): WAIT_FOR_CONCURRENT_GC blocked 11ms
D/dalvikvm(13298): GC_CONCURRENT freed 1294K, 25% free 10901K/14407K, paused 2ms+2ms, total 27ms
D/dalvikvm(13298): WAIT_FOR_CONCURRENT_GC blocked 11ms
D/dalvikvm(13298): GC_CONCURRENT freed 1187K, 22% free 11330K/14407K, paused 2ms+2ms, total 30ms
D/dalvikvm(13298): WAIT_FOR_CONCURRENT_GC blocked 15ms
D/dalvikvm(13298): GC_CONCURRENT freed 1459K, 19% free 11919K/14535K, paused 13ms+4ms, total 64ms
D/dalvikvm(13298): WAIT_FOR_CONCURRENT_GC blocked 18ms
D/ .../downloadmanager.cpp:65 (void DownloadManager::downloadFile(QUrl, QString)): after
I simply don't understand what causes that much GC to happen - it takes about a full to one and a half seconds for everything to work out (for a download that should take a split-second and, moreover, be asynchronus and not locking up the UI).
Additional Information:
It is always only the first download that triggers this. Subsequent downloads, even for the exact same file, work flawlessly
It doesn't matter if there's a file at the exact location with the exact name or not. Downloading the file, deleting it, getting back into the application and redownloading it provides the same results - the first get is slow and has the GC, the second works perfectly fine.
I call all that from a QML File, that causes a singleton c++ object to call DownloadManager::downloadFile.
Other than the QML UI, nothing else is running within the application. No heavy data exchanges, no background loading on other threads, nothing.
I'd be thankful for any pointers towards solving this.
I didn't try on Android, but I had the same issue on Windows. Because those are the same symptoms, I would say this is likely the same reason, which is that the implementation is lazily loading some shared library on the first get() call. This is particularly true when using an encrypted connection; in my case I can see in Visual Studio that 19 DLLs are loaded on the first get() call.
One way to work around that is to pre-connect to the server using connectToHost or connectToHostEncrypted, depending on whether you are using an encrypted connection (e.g. HTTPS) or not. I am calling that at application startup, but anytime the UI is idle should be fine. Then subsequent get() calls will all have the same performance, including the first one, since the libraries have been loaded and the connection is already established. I am assuming that connecting to any server will load the libraries.
See https://forum.qt.io/topic/65201/qnetworkaccessmanager-first-get-very-slow/14 for details on the generic error (not specific to Android).
While resizing large bitmaps for faster image upload to a server I occasionally ran into OutOfMemoryErrors.
To prevent this I calculate the required amount of memory and check if it exceeds Runtime.getRuntime().maxMemory() before trying to scale an image.
However, I still run into OOM errors even though the image should fit on the heap easily.
The emulated device (Galaxy SII API 16) gives me a max memory of 67108864 bytes using the above method.
In the following snippet, the heap size is 43975K and only < 15K of that memory is in use. For my ~31K allocation the heap should grow automatically to about 45K which is still not even close to the maximum size of 64 MiB.
But as you can see, instead of expanding the heap, the dalvik vm runs out of memory.
10-13 20:35:57.223: D/dalvikvm(1201): GC_FOR_ALLOC freed 505K, 67% free 14692K/43975K, paused 31ms, total 31ms
10-13 20:35:57.223: I/dalvikvm-heap(1201): Forcing collection of SoftReferences for 31961100-byte allocation
10-13 20:35:57.251: D/dalvikvm(1201): GC_BEFORE_OOM freed 2K, 67% free 14689K/43975K, paused 29ms, total 29ms
10-13 20:35:57.251: E/dalvikvm-heap(1201): Out of memory on a 31961100-byte allocation.
I wonder if this can happen on a real device too or if this could be a genymotion bug.
Is the heap guaranteed to expand up to maxMemory()? The JavaDoc for Runtime.getRuntime().freeMemory() says it "may" expand, whatever that means.
I just need a realiable way to calculate the amount of memory I can use, this is how I did it, please correct me if I'm wrong:
long maxMemory = Runtime.getRuntime().maxMemory();
long usedMemory = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
long availableMemory = maxMemory - usedMemory;
This call causes the OutOfMemoryError:
// outOptions has an appropriate inSampleSize
BitmapFactory.decodeStream(inputStream, null, outOptions);
Out of memory on a 31961100-byte allocation
Your bitmap is 32M. VM can't allocate 32M linear space to store bitmap. Heap is fragmented, so even if your heap has 32M free space it is not always possible to allocate such linear space. You can try free up as much memory as you can and call GC before decoding stream.
Try to decode your bitmap in more effective way. Or process image in parts.
If you tell us why you need this image, we can tell you how to handle it.
You have a 42MB heap , out of which 14MB is already used, 67% (28M) is free/available
D/dalvikvm(1201): GC_BEFORE_OOM freed 2K, 67% free 14689K/43975K, paused ...
E/dalvikvm-heap(1201): Out of memory on a 31961100-byte allocation.
You are trying to allocate ~31M (not 31K) , which is greater than 28M that is available, resulting in OOM.
For details on interpreting dalvikvm memory allocation log message take a look at debugging memory
There is lot of shared memory usage going on in android, to properly calculate per process memory usage refer this SO question
Android best practices on efficient bitmap memory management may be of help
One thing you might try to tweak is build.props file of the ROM.
On Genymotion emulator you can try executing the following via root shell:
cat /system/build.prop | grep dalvik
and it would display the line with dalvik settings:
dalvik.vm.heapsize=256m
dalvik.vm.lockprof.threshold=500
dalvik.vm.stack-trace-file=/data/anr/traces.txt
And maxmemory is also being reported as 268435456 bytes on the emulator I experimented with.
So, you may try playing with this setting. Also, ensure that the memory allocated in VirtualBox's settings is compatible with these values.
I'm getting way too many GC_FOR_ALLOC from the dalvikvm.
I'm getting XML from a REST service: in one activity I parse about 100 lines programatically(me) and in the other activity I use the SimpleXML to parse about 200 lines.
In the first one I get 50 GC_FOR_ALLOC.
In the second one I get like 300!! (I can't even post it all, the body makes 29579 characters and it's allowed only 30k)
I've searched and almost everyone complains about gc_for_"M"alloc and not gc_for_"A"lloc.
Is the SimpleXML the problem because the instances created?
I'll post the logcat dump by dalvikvm, maybe the values have some information.
Thank you very much for your help.
12-11 06:13:49.564: D/dalvikvm(6759): GC_FOR_ALLOC freed 362K, 13% free 4116K/4688K, paused 181ms, total 182ms
12-11 06:13:50.074: D/dalvikvm(6759): GC_FOR_ALLOC freed 303K, 13% free 4134K/4708K, paused 142ms, total 142ms
.... repeated many times .....
12-11 06:14:06.254: D/dalvikvm(6759): GC_FOR_ALLOC freed 73K, 13% free 4159K/4768K, paused 53ms, total 53ms
12-11 06:14:06.314: D/dalvikvm(6759): GC_FOR_ALLOC freed 103K, 13% free 4159K/4768K, paused 56ms, total 57ms
12-11 06:14:06.374: D/dalvikvm(6759): GC_FOR_ALLOC freed 29K, 12% free 4203K/4768K, paused 54ms, total 54ms
12-11 06:14:06.424: D/dalvikvm(6759): GC_FOR_ALLOC freed 73K, 13% fre
You can see the most-recently-allocated objects using the DDMS Allocation Tracker (memory debugging docs, old blog post, ddms docs). This will show you what's being allocated and give you a stack trace for the place where the allocation is being performed.
Another blog post describes MAT and other relevant tools, though heap-dump analysis is less useful for this sort of problem because it generally shows you the objects that haven't been freed, and you're more interested in the objects that are being freed.
In Android Dalvik VM, GC_FOR_ALLOC is inovked in object alloc step when dlmalloc footprint space is NOT enough for new or heap->bytesAllocated + n > hs->softLimit. You can set dalvik.system.setTargetHeapUtilization lower for more free heap space.
you can use MAT MAT tutorial
to find how many object are creating and garbage collected. so that youcan optimize your code
If you get that multiple GC_FOR_ALLOC while your app is lagging, there is a big possibility that the bug is in a loop. Check where the line of code starts to trigger the GC then start tracing the code from there. In my experience, I mistyped my inner loop iterator which causes the program to make an infinite loop. I created a bug like this:
for(int i=0; i<list.size(); i++) {
for(int j=i+1 j<list.size(); i++) {
// I mistyped the iterator of integer j with i
// making an infinite loop which triggered the GC.
//appears many times
}
}
I encounter the same problem today.
I find a not ended loop in my code such as while(i < xx), but I not implement the i++ statement in the while body.
So the messages like you meet appeared.
Check your code firstly please.
My log:
D/dalvikvm: GC_FOR_ALLOC freed 549K, 9% free 7878K/8596K, paused 30ms, total 34ms
...freed 539K, 9% free 7888K/8596K, paused 30ms, total 30ms
...freed 1856K, 21% free 8083K/10108K, paused 51ms, total 51ms
...freed 582K, 9% free 7845K/8596K, paused 38ms, total 38ms
Explain:
When your app get memory more limit per app. Dalvik/Ant call garbage collector.
What limits memory for your App decide Dalvik/Ant. As you see for my app Dalvik decide 8596K(double case) and 8083K(one case).
And limits change in runtime.
And you can not be sure when this happens. But you can reduce the likelihood. Decreasing the amount of memory that your application consumes.
PS: Decide when call GC teakes Dalvik/Ant. And you can not be sure when this happens. But you can reduce the likelihood. Decreasing the amount of memory that your application consumes.
PS: In "Monitor android" see tab "Monitors", graphics "Memory". And use buttons: "pause(enabled)", Initiate GC, "Dump Java Heap" "Start Alocation Tracking(very useful)". And use official guide for this:
https://developer.android.com/studio/profile/am-memory.html?utm_source=android-studio.
As far as I understand App must not stop/pause working or crashes when VM call GC.
I have a ListView that uses a custom adapter. The custom adapter's getView uses all the recommended practices:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
SuscriptionsViewsHolder holder;
ItemInRootList item = mItemsInList.get(position);
if (convertView == null) {
convertView = mInflater.inflate(R.layout.label, null);
holder = new SuscriptionsViewsHolder();
holder.label = (TextView) convertView.findViewById(R.id.label_label);
holder.icon = (ImageView) convertView.findViewById(R.id.label_icon);
convertView.setTag(holder);
} else {
holder = (SuscriptionsViewsHolder) convertView.getTag();
}
String text = String.format("%1$s (%2$s)", item.title, item.unreadCount);
holder.label.setText(text);
holder.icon.setImageResource(item.isLabel ? R.drawable.folder : R.drawable.file );
return convertView;
}
However when I scroll, it is sluggish because of heavy garbage collection:
GC_EXTERNAL_ALLOC freed 87K, 48% free 2873K/5447K, external 516K/519K, paused 30ms
GC_EXTERNAL_ALLOC freed 7K, 48% free 2866K/5447K, external 1056K/1208K, paused 29ms
GC_EXTERNAL_ALLOC freed <1K, 48% free 2866K/5447K, external 1416K/1568K, paused 28ms
GC_EXTERNAL_ALLOC freed 5K, 48% free 2865K/5447K, external 1600K/1748K, paused 27ms
GC_EXTERNAL_ALLOC freed <1K, 48% free 2865K/5447K, external 1780K/1932K, paused 30ms
GC_EXTERNAL_ALLOC freed 2K, 48% free 2870K/5447K, external 1780K/1932K, paused 26ms
GC_EXTERNAL_ALLOC freed 2K, 48% free 2870K/5447K, external 1780K/1932K, paused 25ms
GC_EXTERNAL_ALLOC freed <1K, 48% free 2870K/5447K, external 1780K/1932K, paused 26ms
GC_EXTERNAL_ALLOC freed 3K, 48% free 2870K/5447K, external 1780K/1932K, paused 25ms
GC_EXTERNAL_ALLOC freed <1K, 48% free 2870K/5447K, external 1780K/1932K, paused 29ms
GC_EXTERNAL_ALLOC freed <1K, 48% free 2870K/5447K, external 1780K/1932K, paused 29ms
GC_EXTERNAL_ALLOC freed <1K, 48% free 2871K/5447K, external 1780K/1932K, paused 28ms
GC_EXTERNAL_ALLOC freed <1K, 48% free 2871K/5447K, external 1780K/1932K, paused 26ms
GC_EXTERNAL_ALLOC freed <1K, 48% free 2870K/5447K, external 1780K/1932K, paused 27ms
GC_EXTERNAL_ALLOC freed <1K, 48% free 2870K/5447K, external 1780K/1932K, paused 29ms
GC_EXTERNAL_ALLOC freed <1K, 48% free 2870K/5447K, external 1780K/1932K, paused 26ms
GC_EXTERNAL_ALLOC freed <1K, 48% free 2870K/5447K, external 1780K/1932K, paused 34ms
What seems to be wrong?
EDIT #12:47 GMT:
In fact it's slightly more complicated than this. My app UI is based on 2 parts. One is the brain of a screen, creating the views, handling user input, etc. The other is a Fragment if the device has android 3.0, otherwise it's an Activity.
The GC happened on my Nexus One 2.3.3 device, so using the Activity. I don't have my Xoom with me to test the behaviour with a Fragment.
I could post the source if required, but let me try to explain it :
RootList is the brain of the UI. It contains :
a List<> of items that will be placed in the ListView.
a method that builds this list from a SQLite db
a custom BaseAdapter that contains basically only the getView method pasted above
RootListActivity is a ListActivity, which:
uses an XML layout
the layout has of course a listview with id android.id.list
the Activity callbacks are forwarded to the RootList class using an instance of RootList created when the activity is created (constructor, not onCreate)
in the onCreate, I call RootList's methods that will create the list of items, and set the list data to a new instance of my custom class derived from BaseAdapter
EDIT on may 17th # 9:36PM GMT:
Here's the code of the Activity and the class that does the things. http://pastebin.com/EgHKRr4r
I found the issue. My XML layout for the activity was:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<include android:id="#+id/rootlist_header" layout="#layout/pre_honeycomb_action_bar" />
<ListView android:id="#android:id/list"
android:layout_below="#id/rootlist_header"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1"
android:textColor="#444444"
android:divider="#drawable/list_divider"
android:dividerHeight="1px"
android:cacheColorHint="#00000000" />
</RelativeLayout>
If I remove the android:cacheColorHint="#00000000", the heavy GC is out, and the scrolling is smooth! :)
I don't really know why this parameter was set, because I don't need it. Maybe I copypasted too much instead of actually build my XML layout.
THANK YOU for your support, I really appreciate your help.
I also had a problem with the android:cacheColorHint="#00000000".
I needed to use it although because I have a fixed background image.
I found that setting the following properties disables android's list view caching and the list scrolls smooth (the GC is not called that often any more):
android:scrollingCache="false"
android:animationCache="false"
Also if u want to get rid of the default selection color use this:
android:listSelector="#00000000"
You should use DDMS and its Allocation Tracker to figure out exactly what's generating so many objects. Note however that String.format() is well known for generating large amounts of garbage.
http://developer.android.com/resources/articles/track-mem.html
I actually went through your code and hacked a bit to make it work on my devices.
I can confirm your ListAdapter is just fine, and that the problem is elsewhere.
This is what you do in the createDefaultLabelsList() method.
mItemsInList.clear();
ItemInRootList item;
do {
item = new ItemInRootList();
item.isLabel = true;
item.id = c.getString(c.getColumnIndex(Labels.KEY_ID));
item.title = Labels.label(item.id);
//TODO: fix this label.label = c.getString(c.getColumnIndex(Labels.KEY_LABEL));
item.unreadCount = c.getString(c.getColumnIndex(Labels.KEY_UNREAD_COUNT));
mItemsInList.add(item);
} while (c.moveToNext());
It seems that not using a local variable in your loop is the cause to your performance problem, believe it or not.
Replace that with:
mItemsInList.clear();
do {
ItemInRootList item = new ItemInRootList();
item.isLabel = true;
item.id = c.getString(c.getColumnIndex(Labels.KEY_ID));
item.title = Labels.label(item.id);
//TODO: fix this label.label = c.getString(c.getColumnIndex(Labels.KEY_LABEL));
item.unreadCount = c.getString(c.getColumnIndex(Labels.KEY_UNREAD_COUNT));
mItemsInList.add(item);
} while (c.moveToNext());
and enjoy your blazing fast list! This solved it for me on htc hero (2.1) and Xoom (3.1) using a ListActivity.
That's how I would have done it in the first place - but I'm not exactly sure how this explains the sluggish behaviour with your original implementation.
I found this out because in order to reproduce your issue I had to replace the way you built the list, just created a list of with 10k random labels and watched it scroll really fast. I then noticed we didn't use the exact same loop, you were adding the same reference over and over again while I was adding 10k different references. I switched to your implementation and reproduced the bug.
this
String text = String.format("%1$s (%2$s)", item.title, item.unreadCount);
will make the GC called many times .
String.format() allocates some temporary memory in addition to the string it finally produces.
another way to do this.
using the StringBuilder class, like this.
StringBuilder builder = new StringBuilder(128);
#Override
public View getView(int position, View convertView, ViewGroup parent) {
SuscriptionsViewsHolder holder;
ItemInRootList item = mItemsInList.get(position);
if (convertView == null) {
convertView = mInflater.inflate(R.layout.label, null);
holder = new SuscriptionsViewsHolder();
holder.label = (TextView) convertView.findViewById(R.id.label_label);
holder.icon = (ImageView) convertView.findViewById(R.id.label_icon);
convertView.setTag(holder);
} else {
holder = (SuscriptionsViewsHolder) convertView.getTag();
}
String text = String.format("%1$s (%2$s)", item.title, item.unreadCount);
builder.setLength(0);
builder.append(item.title).append(" (").append(item.unreadCount).append(")");
holder.label.setText(builder.toString());
holder.icon.setImageResource(item.isLabel ? R.drawable.folder : R.drawable.file );
return convertView;
}
String text = String.format("%1$s (%2$s)", item.title, item.unreadCount);
May be String.format() creates a lot of intermediate strings that pollutes the GC. Try StringBuilder and try to cache string it builds.
Also as #tmho said try
holder.icon.setImageDrawable(); instead of holder.icon.setImageResource();
because setImageResource() creates Drawable object each time. And each Drawable is about 50-60 bytes. It seems like heave bitmap isn't duplicated in this case, just drawable container but it may be enough to cause periodical GC calls.
try holder.icon.setImageBitmap(); instead of holder.icon.setImageResource();
This begins with 4.4.4 KitKat update. Rerun code already normal code on it and this problem appeared. ListView is completely unusable now due to very slow scroll. (Nexus 4)
And I'm not using any formatters on it.
When View is being scrolled numerous GC message appears.
I got similar question, my listview will show images from internet, and save them on SD card. When app launch later, local image will be used, then when i scroll the listview, lots of GC appear on logcat, and the view will stuns slightly.
I noticed that when app was launched first time, with images all from internet, the listview shows normally.
By the way, I used the android-imagedownloader from http://code.google.com/p/android-imagedownloader/, and add some local file cache code.
So here is my solution, when it loads image from local file, i will add the bitmap object to memory cache(a static HashMap), after that, when the listview scroll up and down, it will get image from memory cache, and lots GC did not happen again.