静态引用都被清除 - 没有Android的卸载类在运行时,如果未使用的?静态、Android

2023-09-12 07:19:19 作者:吃不饱

我有具体的类加载/垃圾收集如何在Android的一个问题。我们偶然发现了这个问题,现在几次,而据我所知,Android的表现不同,这里从一个普通的JVM。

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.

问题是这样的:我们目前正在努力减少对单类的应用程序支持单根工厂单身其唯一目的是管理其他管理类。一个顶级的经理,如果你会的。这使得我们很容易更换的测试实现,而不选择了一个完整的DI解决方案,因为所有的活动和服务共享相同的引用,根工厂。

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.

下面是它的样子:

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;
    }

    // ...
}

初始化被调用一次,在 Application.onCreate ,即在的任何活动或服务已启动。现在,这里的问题是:在的getInstance 方法有时回来为 - 在同一个线程调用,即使!这听起来像它不是一个可见度的问题;相反,在一流水平的静态单参考保持似乎确实已被清除的垃圾收集器。也许我过早下结论,在这里,但可能这是因为Android的垃圾收集器或类加载机制可以真正的卸载的类时内存变得稀缺,在这种情况下,唯一提及的一个实例会远?我真的不深入到Java的内存模型,但我想这不应该发生,否则实施单身的这种常见的方式不会对任何JVM吧?

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:一是可以解决此通过保持对单一应用程序实例全局,而不是引用。这已被证明是可靠的,当一个必须通过一个应用程序的整个生命周期保持对​​对象周围。

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.

更新

显然,我使用volatile这里造成了一定的混乱。我的目的是确保静态引用的当前状态始终是可见的访问它的所有线程。我必须这样做,因为我既写入和读取从多个线程参考:在刚刚在主应用程序线程一个普通的应用程序运行,但在仪表试运行,其中的对象被替换为嘲笑,我从写仪表线程读取它的UI线程。我本来也同步调用的getInstance ,但这是更昂贵,因为它需要自称对象锁。请参阅What是用Java实现Singleton模式的有效途径?,以解决这个更详细的讨论。

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.

推荐答案

无论你(@Matthias)和马克·墨菲(@CommonsWare)是你说什么正确的,但关键似乎失去。 (使用挥发性是正确的类不卸载。)

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:

您正在呼吁从活动初始化 的Andr​​oid需要更多的内存,杀死了整个进程 Android的重新启动应用程序和顶部活动 您拨打的getInstance 将返回,因为初始化不叫 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

纠正我,如果我错了。