30

I have a question specific to how the classloading / garbage collection works in Android. We have stumbled upon this issue a few times now, and as far as I can tell, Android behaves different here from an ordinary JVM.

The problem is this: We're currently trying to cut down on singleton classes in the app in favor of a single root factory singleton which sole purpose is to manage other manager classes. A top level manager if you will. This makes it easy for us to replace implementations in tests without opting for a full DI solution, since all Activities and Services share the same reference to that root factory.

Here's how it looks like:

public class RootFactory {

    private static volatile RootFactory instance;

    @SuppressWarnings("unused")
    private Context context; // I'd like to keep this for now

    private volatile LanguageSupport languageSupport;
    private volatile Preferences preferences;
    private volatile LoginManager loginManager;
    private volatile TaskManager taskManager;
    private volatile PositionProvider positionManager;
    private volatile SimpleDataStorage simpleDataStorage;

    public static RootFactory initialize(Context context) {
        instance = new RootFactory(context);
        return instance;
    }

    private RootFactory(Context context) {
        this.context = context;
    }

    public static RootFactory getInstance() {
        return instance;
    }

    public LanguageSupport getLanguageSupport() {
        return languageSupport;
    }

    public void setLanguageSupport(LanguageSupport languageSupport) {
        this.languageSupport = languageSupport;
    }

    // ...
}

initialize is called once, in Application.onCreate, i.e. before any Activity or Service is started. Now, here is the problem: the getInstance method sometimes comes back as null -- even when invoked on the same thread! That sounds like it isn't a visibility problem; instead, the static singleton reference hold on class level seems to actually have been cleared by the garbage collector. Maybe I'm jumping to conclusions here, but could this be because the Android garbage collector or class loading mechanism can actually unload classes when memory gets scarce, in which case the only reference to the singleton instance will go away? I'm not really deep into Java's memory model, but I suppose that shouldn't happen, otherwise this common way of implementing singletons wouldn't work on any JVM right?

Any idea why this is happening exactly?

PS: one can work around this by keeping "global" references on the single application instance instead. That has proven to be reliable when one must keep on object around across the entire life-time of an app.

UPDATE

Apparently my use of volatile here caused some confusion. My intention was to ensure that the static reference's current state is always visible to all threads accessing it. I must do that because I am both writing and reading that reference from more than one thread: In an ordinary app run just in the main application thread, but in an instrumentation test run, where objects get replaced with mocks, I write it from the instrumentation thread and read it on the UI thread. I could have as well synchronized the call to getInstance, but that's more expensive since it requires claiming an object lock. See What is an efficient way to implement a singleton pattern in Java? for a more detailed discussion around this.

Community
  • 1
  • 1
mxk
  • 43,056
  • 28
  • 105
  • 132
  • 1
    I've seen this sort of behaviour too while running from Instrumentation. I'd be interested in seeing an answer. But thanks for the hint about `volatile`; I'll have a look at that. – Christopher Orr Feb 24 '11 at 19:49
  • "initialize is called once, in Application.onCreate, i.e. before any Activity or Service is started" --> any refs on this ? – Mr_and_Mrs_D Oct 15 '13 at 14:18
  • 1
    One useful step would be to add logging to `initialize()` and `getInstance()`, and confirm that the latter is never called before the former. Also, add logging to anything that updates `instance`. The garbage collector does not NULL fields out, and even if Android did unload classes it would only do so if *all* classes loaded by the same ClassLoader could be discarded. Note that all static fields are essentially "reset" when the app is killed and restarted by the system. – fadden Oct 15 '13 at 18:26
  • Did you ever get to the bottom of this? FYI: an example of a static volatile is presented in Effective Java 2nd Edition on page 262, Item 66. As mentioned already, volatile is required to ensure that the most recent value of instance is visible to all threads. Though, it may be worth using an enum or another synchronization technique. As it stands, nothing prevents clients from violating the assumed singleton invariant because every call to initialize will create another instance. – James Wald Nov 22 '13 at 05:02

4 Answers4

5

Both you (@Matthias) and Mark Murphy (@CommonsWare) are correct in what you say, but the gist seems lost. (The use of volatile is correct and classes are not unloaded.)

The crux of the question is where initialize is called from.

Here is what I think is happening:

  • You are calling initialize from an Activity *
  • Android needs more memory, kills the whole Process
  • Android restarts the Application and the top Activity
  • You call getInstance which will return null, as initialize was not called

Correct me if I'm wrong.


Update:
My assumption – that initialize is called from an Activity * – seems to have been wrong in this case. However, I'll leave this answer up because that scenario is a common source of bugs.

P Varga
  • 19,174
  • 12
  • 70
  • 108
  • 1
    When restarting the application instance, onCreate should be called, even if its inflating the Activity rather than starting your default Activity (this is a source of many errors. Initializing in a "splash screen" Activity instead of the Application instance). Did anybody figure this out? I try to minimize statics, but we use them. Have heard a rumor about class unloading, but I tend to agree with @CommonsWare. Classes are unloaded when VM takes a dirt nap. Not before. – Kevin Galligan Oct 30 '12 at 00:05
  • 1
    Interesting - I didn't realize Android re-created only the topmost Activity on process kill and restart. – Adam Johns Jul 24 '19 at 14:02
4

I have never in my life seen a static data member declared volatile. I'm not even sure what that means.

Static data members will exist until the process is terminated or until you get rid of them (e.g., null out the static reference). The process may be terminated once all activities and services are proactively closed by the user (e.g., BACK button) and your code (e.g., stopService()). The process may be terminated even with live components if Android is desperately short on RAM, but this is rather unusual. The process may be terminated with a live service if Android thinks that your service has been in the background too long, though it may restart that service depending on your return value from onStartCommand().

Classes are not unloaded, period, short of the process being terminated.

To address the other of @sergui's points, activities may be destroyed, with instance state stored (albeit in RAM, not "fixed storage"), to free up RAM. Android will tend to do this before terminating active processes, though if it destroys the last activity for a process and there are no running services, that process will be a prime candidate for termination.

The only thing significantly strange about your implementation is your use of volatile.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • 7
    We never null the static reference. As far as I remember, `volatile` causes a flush of memory shared between threads. Using volatile here therefore makes sure that the reference is always visible to all threads accessing it. If volatile is not used, the instrumentation thread may write to that reference but the UI thread may still see it as null (since memory was not synchronized). Not sure what you find odd about that. Can you elaborate? – mxk Feb 24 '11 at 13:31
  • 1
    Also, I was a bit puzzled by your "have never in my life seen a static data member declared volatile" remark, so I double checked whether it was me getting something really wrong here, but have a look at that: http://en.wikipedia.org/wiki/Singleton_pattern ("Traditional solution"). I think volatile makes sense here, and is not the cause of the problem. – mxk Feb 24 '11 at 13:36
  • 2
    @Matthias: I know what `volatile` means. I have never seen `volatile` used on a static data member before, and your Wikipedia reference does not indicate whether there are different characteristics of a `volatile` static versus a `volatile` non-static. It seems like most people nowadays use `synchronized` or `java.util.concurrent` to make things thread-safe. This does not mean that your use of `volatile` is wrong. However, it is the one unusual thing I see in your implementation, and therefore is a candidate for somehow being the source of your difficulty. – CommonsWare Feb 24 '11 at 13:39
  • @Matthias: Your second Wikipedia link does not address your use of `volatile`. I apologize for trying to assist you with your problem. – CommonsWare Feb 24 '11 at 13:40
  • Mark, granted, it's mentioned only between the lines, but it does mention volatile whenever you cannot use final. This happens e.g. when you cannot instantiate the instance upon class load, which is the case for us, since we must replace the implementation with a mocked implementation from another thread--the instrumentation thread. Maybe that was not entirely clear from my initial post, sorry for that. – mxk Feb 24 '11 at 13:44
  • Apart from the volatile thing (which I'm fairly sure has nothing to do with the problem I'm seeing, quite the opposite in fact), do you have any links to material backing your "classes are not unloaded short of the process being terminated" claim? I mean, I am seeing my reference getting cleared without me setting it to null, so there must be more to this. – mxk Feb 24 '11 at 13:48
  • @Matthias: http://stackoverflow.com/questions/2447588/android-when-do-classes-get-unloaded-by-the-system – CommonsWare Feb 24 '11 at 13:55
  • 1
    Thanks for the link. If what fadden says in that question is correct, then I'm still pretty much where I left off. Again I don't think it has anything to do with concurrency and thread synchronization, since it's happening on the very same thread, the UI thread. So if I set a static member, and it's gone a second later, what else could be causing that? – mxk Feb 24 '11 at 14:05
  • 1
    (I've updated my question with an explanation as to why I'm using volatile, since that seemed to have stirred up some confusion) – mxk Feb 24 '11 at 14:10
  • 1
    @Matthias: "If what fadden says in that question is correct" -- considering that he probably wrote the relevant portions of Dalvik, yes, I imagine that he is correct. – CommonsWare Feb 24 '11 at 14:20
2

I've seen similar strange behaviour with my own code involving disappearing static variables (I don't think this problem has anything to do with the volatile keyword). In particular this has come up when I've initialized a logging framework (ex. Crashlytics, log4j), and then after some period of activity it appears to be uninitialized. Investigation has shown this happens after the OS calls onSaveInstanceState(Bundle b).

Your static variables are held by the Classloader which is contained within your app's process. According to google:

An unusual and fundamental feature of Android is that an application process's lifetime is not directly controlled by the application itself. Instead, it is determined by the system through a combination of the parts of the application that the system knows are running, how important these things are to the user, and how much overall memory is available in the system.

http://developer.android.com/guide/topics/processes/process-lifecycle.html

What that means for a developer is that you cannot expect static variables to remain initialized indefinitely. You need to rely on a different mechanism for persistence.

One workaround I've used to keep my logging framework initialized is for all my Activities to extend a base class where I override onCreate and check for initialization and re-initialize if necessary.

I think the official solution is to use the onSaveInstanceState(Bundle b) callback to persist anything that your Activity needs later, and then re-initialize in onCreate(Bundle b) when b != null.

Google explains it best:

http://developer.android.com/training/basics/activity-lifecycle/recreating.html

codemonkey
  • 1,213
  • 2
  • 14
  • 31
2

Static references are cleared whenever the system feels like it and your application is not top-level (the user is not running it explicitly). Whenever your app is minimized and the OS wants some more memory it will either kill your app or serialize it on fixed storage for later use, but in both cases static variables are erased. Also, whenever your app gets a Force Close error, all statics are erased as well. In my experience I saw that it's always better to use variables in the Application object than static variables.

zrgiu
  • 6,200
  • 1
  • 33
  • 35
  • 4
    "Static references are cleared whenever the system feels like it and your application is not top-level (the user is not running it explicitly)." -- false. "or serialize it on fixed storage for later use" -- false. – CommonsWare Feb 24 '11 at 13:16
  • 1
    @CommonsWare: can you please explain then why static variables get erased when the app is minimized ? (tapping the icon will bring the app to it's last state though, without the statics) – zrgiu Feb 24 '11 at 13:21
  • @sergui: Static variables are not "erased when the app is minimized". Please see my answer to this question for more. – CommonsWare Feb 24 '11 at 13:22