I'm trying to track down an issue where some users have started getting out of memory errors in KitKat. I've not seen them in previous versions but I know memory usage has sometimes got much higher than I'd expect.
I'm trying to go through the app identifying things that are stored in static variables at the moment within the app and there's a few that I was wondering if they would cause issues.
They are:
Notification //as in when a notification is raise
PendingIntents
BroadcastReceiver //as in the object passed to: context.registerReceiver
SharedPreferences
From what I can tell I don't think these hold the context objects, but I'm not sure.
I try to make sure when using context objects I'm only using Activity contexts when the scope is only applicable for the activity and when that context is needed. For other situations I use the application context.
I would suggest you to run your app and use all activities your app contains of and also switching orientation several times, then exit the app and trigger a GC (there's buttons for all of this) after that dump a hprof (this asumes you are using Eclipse) and fire the leak suspects report, use the histogram button to view what allocations have been done and sort of #objects (try to filter on your package name since String / byte[] etc tend to dominate such a leak suspect report even though the don't have to be leaks). A guide to: debugging memory in Android.
As for your questions to those specific classes (when i say, "could leak a context" i mean activty context, since application is singleton):
Notifications are preferably handled by the Notification.Builder, that one takes a context so holding on to notification in a static variable could leak a context
PendingIntents are obtained from helper methods where context are sent in see PendingIntent could leak context as well
BroadcastReceivers can be registered in manifest (not leaking context here) or at run time, if you are not unregistering in onPause (registering in onResume) you could leak the BroadcastReceiver which in turn leaks the current activity context (this)
SharedPreferences are fetched from the current activity if your activity gets recreated there might be a leak as well
This is a hard topic, that's why i suggest you to profile your are app i suggest in the top of this answer. Other than that i don't quite get the point of holding on to several of the asked classes as static instnace variables, what do you want to achieve with that? Especially if you are not sure if you leak contexts or not.
Related
Why I shouldn't use static objects in an Activity or don't make static calls to an Activity?
A more reasonable statement would be saying be extremely careful about using static variables in Android.
You can use them, but be aware that you application can and will be killed by the OS, and restarted when the user returns to the app (i.e. maybe from the recent apps list). This results in your application having many different entry points, and you can't assume the static variable will be initialized.
For example, setting a static variable in your application's first Activity, and assuming it will always be set is a big mistake.
Also, be cautious about storing anything that has a reference to an Activity as a static variable, because this tends to be a common source of consuming memory unnecessarily. For example, storing a View in a static variable is almost certainly a mistake, because it will prevent an entire Activity from being garbage collected if not cleared out.
It is a general good practice to avoid making things static that don't need to be since they increase the chances of memory leaks. If you're always holding a reference to some data the GC won't be able to free it.
I have some unclear situation:
Will static singletons be garbage collected after last reference holding Activity has been destroyed? Because there is no more references in Application to singleton instance. Can I then rely on singletons?
By official Android doc:
There is normally no need to subclass Application. In most situation,
static singletons can provide the same functionality in a more modular
way.
By some post:
https://web.archive.org/web/20160729201921/http://www.devahead.com/blog/2011/06/extending-the-android-application-class-and-dealing-with-singleton/
While developing an application, I found
that sometimes some static variables bound to activities happened to
be uninitialized even though they’ve previously been initialized! I
thought that when a static variable is initialized it stays so for the
entire life of the application, but this doesn’t seem to be the case.
In another words, if nothing is holding a reference to my static singleton class, what's to prevent it from being garbage collected and destroyed?
No, because if it's a singleton, it's stored as a static field in its class, and usually singletons are not destroyed by clients, ie you wouldn't put a static method deleteInstance() which sets the reference to null so that if nobody else uses it, it's eligible for garbage collection. For static fields, garbage collection will happen when the classloader which loaded the class is discarded.
For this reason, the keyword static itself may cause memory leaks, if it references Activity objects, so you should be very careful when using it.
Yes. Every time you leave your application (for example your app opens the camera app to take a picture, or opens the browser to open a link, or the user just pushes the back button) there is a possibility that your Application object will be destroyed and recreated when you navigate back to your app.
You should initialize any static variable in a static {} block in your custom Application class if you have one, or in your Activities to ensure they won't be null.
Note that it is more probable to experience this issue on devices with weaker hardware, lower memory, but you should expect it can happen on any device.
Bottom line is, in android, don't expect that your static variables will stay in the memory at any time. Always check if they exist and reinitialize them if necessary at the right places.
EDIT:
I know it has been a long time, and I totally forgot about this thread, anyway, here is the source from the official Android lifecycle documentation:
http://developer.android.com/training/basics/activity-lifecycle/recreating.html
You can not control when exactly Java objects become garbage collected. An object becomes eligible for garbage collection when there are no more (non-circular) references to it. With Android, further, you can not control when your Activity gets removed from memory.
singletons are supposed to represent something which always exist.
You can't force any object to be garbage collected; you can request that the garbage collector runs with System.gc() but it's only a request.
If you want to make a "singleton" eligible for garbage collection, you'd probably want to have a method to set the static variable to null (and hope that nothing else had taken a copy of the reference). Obviously the next time anyone asked for an instance, it would need to be recreated. at which point it's not really a singleton, of course.
All singleton object will stay even if the activity is destroyed.
I am very familiar with the Android activity lifecycle, but I feel like I am missing something that should be pretty obvious here.
I have a multi-activity Android app; it uses static variables (in the application class) to keep a handle on the various objects that are used across the different views. The problem occurs when the app is paused (exited) and Android (presumably) cleans up memory to maintain foreground processes. When the user resumes the app, it occasionally (not always) seems to come back and resume in the Activity that the user left, but with the static variables nulled. Even worse, the activity sometimes seems to resume with the static variables still present, but with internal variables in objects nulled.
I eventually implemented a simple sanity check, which checks that the statics are not nulled (and also the most vital inner variables of relevant objects) and returns the app to start if it fails. This did cut down on a lot on the problems, but I still see the occasional issues with this, as it is simply not practical to check everything for every resume().
What I really need is for the app to restart from scratch if the Android OS decides it needs to clean anything non-GC from memory while the app is inactive. I feel there should be a graceful way to do this, but haven't noticed anything in the documentation.
Any thoughts? How do others avoid this problem?
Using the Application class for preserving state can result in unexpected behaviour if Android decides to kill your process completely. Check out this answer to a similar question
i.e. you should probably use some sort of persistence (SharedPreferences or some DB) in your Activity lifecycle callbacks.
I´ve an Android Application that holds some static objects on an class that extends Application class, using the same approach as exemplified here.
The objects that is hold by this class is shared and manipulated between all activities on my app.
Everything works well, but, some time ago, I noticed that when the application runs on backgroud for some time, when it´s restored, the data that was stored on the extended class has gone, and the app starts to throws a lot of NullReference exceptions.
I think that this happens because of the application was being temporary destroyed by the OS, to be recreated when we need to use it again.
So, how could I handle this scenario? Is there any way to discover that the application is being temporary destroyed, without subscribing to the onDestroy event of an Activity? On a test that I did, the onDestroy event was not called when I asked the background process of my app to being stopped.
Thanks a lot!
There is no way to determine when the proccess will be killed, so you always should store important data somewhere(sd-card for example) and restore it in onCreate() method of your App class.
Also take a look at onLowMemory() and onTrimMemory(), release all unnecesary data in memory to help the OS prevent destroying your app, cause one of the reason to determinate your app is a lack of memory.
No, there's no way to tell when you need to persist data that you store in static variables like that. There's no callback that the systems notifies you about this, at least to my knowledge.
So you should only use static variables to store temporary data, or cache data accessed from peristent sources. I've faced this problem in many of my projects, and I always ended up using intents / shared prefs / sqlite / etc. to reliable store data across Activities.
I have a fairly complex app -
The UI Activity launches a Service and sets up 2 way AIDL callbacks so the two can communicate.
The service runs forever (unless hits 'off' button in UI Activity).
The problem is when the user exits the Activity, the Activity memory is not released.
-The amount of memory used by my app is the same even if the UI Activity has been closed.
-The heap still shows mapTiles and other UI crap on it even if the UI Activity has been closed.
So my guess is somehow the Service is holding references to the Activity. I know the many articles warning about leaking the Activity Context. The Service only references the Activity through a Weak Reference stored at Application scope. (a class that extends Application)
Is there any way to find what specifically is referencing the Activity? The dominator tree shows mapTiles and ListView layouts eating all my memory... but I can't find the reference to the Activity which is keeping that stuff alive.
Also, is there a way to dump an HPROF heap dump thing if OutOfMemoryException occurs?
The problem is when the user exits the
Activity, the Activity memory is not
released.
There is no "exit Activity" in Android. Calling finish() or pressing back on the activity does not remove objects from memory that the activity used. Android inherently takes care of this itself at some random point in the future.
If you want your objects to be released from memory at the moment the activity finishes, you need to manually release them yourself.
Edit: The class that can be used to find memory usage is the ActivityManager.
If it's not yourself who is holding a reference ("references the Activity through a Weak Reference stored at Application scope", do you null it? I wonder why you reference the activity in the service, usually its the other way), did you try to execute a GC? As long as there is enough memory left, no gc will take place to move the trash away.