静态引用被清除 - 如果未使用,Android会在运行时卸载类吗?

Mat*_*ias 29 java singleton android garbage-collection classloader

我有一个特定的问题,如何在Android中运行类加载/垃圾收集.我们现在偶然发现了几次这个问题,据我所知,Android在这里与普通的JVM不同.

问题在于:我们目前正在尝试减少应用程序中的单例类,以支持单个根工厂单例,其唯一目的是管理其他管理器类.如果你愿意的话,是一名顶级经理.这使我们可以轻松地替换测试中的实现,而无需选择完整的DI解决方案,因为所有活动和服务共享对该根工厂的相同引用.

这是它的样子:

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

    // ...
}
Run Code Online (Sandbox Code Playgroud)

initialize被调用一次,Application.onCreate任何活动或服务启动之前.现在,问题出现了:即使在同一个线程上调用,该getInstance方法有时也会回来null!听起来这不是一个能见度问题; 相反,类级别上的静态单例引用保持似乎实际上已被垃圾收集器清除.也许我在这里得出结论,但这可能是因为Android垃圾收集器或类加载机制实际上可以在内存稀缺时卸载类,在这种情况下,对单例实例的唯一引用将消失?我并不是真的深入了解Java的内存模型,但我认为这不应该发生,否则这种实现单例的常用方法对任何JVM都不起作用吗?

知道为什么会发生这种情况吗?

PS:可以通过在单个应用程序实例上保留"全局"引用来解决此问题.事实证明,当应用程序必须在应用程序的整个生命周期内保持对象时,这是可靠的.

UPDATE

显然我在这里使用volatile会引起一些混乱.我的目的是确保静态引用的当前状态始终对访问它的所有线程可见.我必须这样做,因为我正在编写并从多个线程中读取该引用:在一个普通的应用程序中,只在主应用程序线程中运行,但在一个检测测试运行中,对象被mocks替换,我从检测线程并在UI线程上读取它.我也可以同步调用getInstance,但这更昂贵,因为它需要声明对象锁定.请参阅在Java中实现单例模式的有效方法是什么?有关此问题的更详细讨论.

ᆼᆺᆼ*_*ᆼᆺᆼ 5

您(@Matthias)和Mark Murphy(@CommonsWare)的说法都是正确的,但要点似乎很失落。(对的使用volatile是正确的,不会卸载类。)

问题的症结在于从何initialize而来。

我认为这是正在发生的事情:

  • 您正在从*调用初始化Activity
  • Android需要更多的内存,扼杀整个 Process
  • Android重新启动ApplicationActivity
  • 你叫getInstance这将返回null,因为initialize当时不叫

如果我错了纠正我。


更新
我的假设- initializeActivity *调用-在这种情况下似乎是错误的。但是,我将保留这个答案,因为这种情况是错误的常见来源。


zrg*_*giu 2

只要系统愿意并且您的应用程序不是顶级的(用户没有显式运行它),静态引用就会被清除。每当您的应用程序最小化并且操作系统需要更多内存时,它就会杀死您的应用程序或将其序列化到固定存储上以供以后使用,但在这两种情况下静态变量都会被删除。此外,每当您的应用程序出现Force Close错误时,所有静态信息也会被删除。根据我的经验,我发现在应用程序对象中使用变量总是比静态变量更好。

  • “每当系统感觉需要并且您的应用程序不是顶级(用户没有显式运行它)时,静态引用就会被清除。” - 错误的。“或者将其序列化到固定存储上以供以后使用”-- false。 (4认同)