静态引用被清除 – 如果未使用,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; } // ... } 

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

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

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

UPDATE

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

Solutions Collecting From Web of "静态引用被清除 – 如果未使用,Android是否会在运行时卸载类?"

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

静态数据成员将一直存在,直到进程终止或直到你摆脱它们为止(例如,将静态引用置null )。 一旦所有活动和服务被用户主动关闭(例如,BACK按钮)和您的代码(例如, stopService() ),就可以终止该过程。 如果Android在RAM上非常短暂,即使使用实时组件也可以终止该过程,但这是相当不寻常的。 如果Android认为您的服务已经在后台工作太长时间,则可以使用实时服务终止该过程,尽管它可能会重新启动该服务,具体取决于您从onStartCommand()返回的值。

类没有被卸载,期间,没有终止进程。

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

你的实现唯一有点奇怪的是你使用volatile

你(@Matthias)和Mark Murphy(@CommonsWare)的说法都是正确的,但要点似乎丢失了。 ( volatile的使用是正确的,类没有卸载。)

问题的关键在于从哪里调用initialize

以下是我的想法:

  • 您正在调用Activity初始化
  • Android需要更多内存,杀死整个Process
  • Android重新启动Application和顶级Activity
  • 您调用getInstance将返回null ,因为未调用initialize

如我错了请纠正我。

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

我已经看到类似的奇怪行为与我自己的代码涉及消失的静态variables(我不认为这个问题与volatile关键字有任何关系)。 特别是当我初始化一个日志框架(例如Crashlytics,log4j),然后经过一段时间的活动后,它似乎未初始化。 调查显示,在OS调用onSaveInstanceState(Bundle b)之后会发生这种情况。

您的静态variables由类加载器保存,该类加载器包含在应用程序的进程中。 根据谷歌:

Android的一个不寻常的基本特征是应用程序进程的生命周期不是由应用程序本身直接控制的。 相反,它由系统通过系统知道正在运行的应用程序部分的组合,这些事物对用户的重要性以及系统中可用的总体内存量来确定。

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

这对开发人员意味着你不能指望静态variables无限期地保持初始化。 您需要依赖不同的持久性机制。

我用来保持我的日志框架初始化的一个解决方法是让我的所有活动扩展基类,我覆盖onCreate并检查初始化并在必要时重新初始化。

我认为官方解决方案是使用onSaveInstanceState(Bundle b)回调来保留Activity稍后需要的任何内容,然后在b != null时在onCreate(Bundle b)重新初始化。

谷歌解释得最好:

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