静态引用被清除 – Android是否在运行时卸载类如果不使用?

我有一个具体的问题,如何在Android中的类加载/垃圾收集工作。 现在我们偶然发现了这个问题,就我所知,Android在这里与普通的JVM有所不同。

问题是这样的:我们目前正在尝试减less应用程序中的单例类,转而使用单一的单根工厂单例,其唯一目的是pipe理其他pipe理器类。 如果您愿意的话,可以成为顶级经理 这使得我们可以轻松地replacetesting中的实现,而无需select完整的DI解决scheme,因为所有活动和服务都与该根工厂共享相同的参考。

以下是它的样子:

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被调用一次,在Application.onCreate ,也就是任何Activity或Service开始之前。 现在,问题在于: getInstance方法有时会返回为null – 即使在同一个线程中调用! 这听起来像不是一个可见性问题; 相反,在类级别上的静态单例引用保持似乎实际上已被垃圾收集器清除。 也许我在这里得出结论,但是这可能是因为Android内存垃圾收集器或类加载机制实际上可以在内存稀缺的时候卸载类,在这种情况下,对单例实例的唯一引用将会消失? 我并没有深入Java的内存模型,但我想这不应该发生,否则这种实现单身的常见方式不适用于任何JVM?

任何想法,为什么这是完全发生?

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

UPDATE

显然我在这里使用的易变性引起了一些混淆。 我的目的是确保静态引用的当前状态总是可见的所有线程访问它。 我必须这样做,因为我正在从多个线程中读取和引用这个引用:在一个普通的应用程序中,只运行在主应用程序线程中,但是在一个工具testing运行中,对象被replace为mock,我从检测线程并在UI线程上读取它。 我也可以将调用同步到getInstance ,但是这样会更昂贵,因为它需要声明一个对象锁。 请参阅在Java中实现单例模式的有效方法是什么? 有关这方面的更详细的讨论。

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

我从来没有见过一个声明为volatile的静态数据成员。 我甚至不知道这是什么意思。

静态数据成员将存在,直到进程终止或直到你摆脱它们(例如,静态引用为null )。 一旦所有活动和服务被用户主动closures(例如返回button)和您的代码(例如stopService() ),该过程可能会终止。 如果Android在内存上绝对短缺,那么即使使用活动组件也可能会终止进程,但这是非常不寻常的。 如果Android认为你的服务已经过了很长时间,这个过程可能会终止,如果你的服务已经在后台运行了很长时间,虽然它可能会根据onStartCommand()返回值重新启动服务。

类不会被卸载,期间不会被终止。

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

关于你的实现唯一很奇怪的是你使用volatile

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

问题的症结在于从何处调用initialize

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

  • 您正在从一个Activity调用初始化
  • Android需要更多的内存,杀死整个Process
  • Android重新启动Application和顶部的Activity
  • 你调用getInstance将返回null ,因为initialize没有被调用

如我错了请纠正我。

静态引用在系统感觉和你的应用程序不是顶层(用户没有明确运行)时被清除。 每当你的应用程序被最小化,操作系统需要更多的内存,它将杀死你的应用程序或将其序列化在固定的存储空间供以后使用,但在这两种情况下,静态variables都将被擦除。 此外,每当你的应用程序得到一个Force Close错误,所有的静态消除以及。 根据我的经验,我发现在Application对象中使用variables总比静态variables好。

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

您的静态variables由您的应用程序进程中包含的Classloader保存。 根据谷歌:

Android的一个不寻常和基本的特点是应用程序的生命周期不是由应用程序本身直接控制的。 而是由系统通过系统知道正在运行的应用程序的各个部分的组合来确定,这些事情对用户有多重要,以及系统中有多less内存可用。

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

对开发者来说意味着你不能期望静态variables无限期地保持初始化。 您需要依靠不同的持久性机制。

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

我认为官方的解决scheme是使用onSaveInstanceState(Bundle b)callback来保存你的Activity以后需要的任何东西,然后在b != null时在onCreate(Bundle b)重新初始化。

谷歌最好的解释:

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