静态引用被清除 - Android 是否会在运行时卸载未使用的类?

我有一个特定于类加载/垃圾回收在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;
    }

    // ...
}

initialize在 中调用一次,即在任何活动或服务启动之前。现在,这就是问题所在:该方法有时会返回为 - 即使在同一线程上调用也是如此!这听起来不是可见性问题。相反,类级别的静态单例引用保留似乎实际上已被垃圾回收器清除。也许我在这里匆匆下结论,但这可能是因为Android垃圾收集器或类加载机制实际上可以在内存稀缺时卸载类,在这种情况下,对单例实例的唯一引用将消失?我并没有真正深入研究Java的内存模型,但我认为这不应该发生,否则这种实现单例的常见方式在任何JVM上都不起作用,对吧?Application.onCreategetInstancenull

任何想法为什么会发生这种情况?

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

更新

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


答案 1

你(@Matthias)和马克·墨菲(@CommonsWare)在你所说的话中都是正确的,但要点似乎丢失了。(使用是正确的,类不会被卸载。volatile

问题的症结在于从哪里被召唤。initialize

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

  • 您正在从Activity *
  • 安卓需要更多内存,杀了整体Process
  • 安卓重启和顶部ApplicationActivity
  • 您调用哪个将返回,因为未调用getInstancenullinitialize

如果我错了,请纠正我。


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


答案 2

我一生中从未见过静态数据成员声明。我甚至不确定这意味着什么。volatile

静态数据成员将一直存在,直到进程终止或直到您摆脱它们(例如,脱离静态引用)。一旦用户主动关闭所有活动和服务(例如,后退按钮)和您的代码(例如,),该过程可能会被终止。如果Android的RAM极度短缺,即使使用实时组件,该过程也可能被终止,但这是相当不寻常的。如果 Android 认为您的服务在后台运行的时间过长,则该过程可能会因实时服务而终止,尽管它可能会重新启动该服务,具体取决于您从 返回的值。nullstopService()onStartCommand()

类不会被卸载,周期短于被终止的进程。

为了解决@sergui的其他问题,可能会销毁活动,并存储实例状态(尽管存储在RAM中,而不是“固定存储”)以释放RAM。Android倾向于在终止活动进程之前执行此操作,但是如果它破坏了进程的最后一个活动并且没有正在运行的服务,则该进程将成为终止的主要候选者。

您的实现唯一明显奇怪的是您使用了 .volatile


推荐