У меня вопрос, связанный с тем, как сборка/загрузка мусора работает в Android. Мы несколько раз наткнулись на эту проблему, и, насколько я могу судить, Android ведет себя по-другому от обычной JVM.
Проблема заключается в следующем: мы в настоящее время пытаемся сократить классы singleton в приложении в пользу единственного root factory singleton, единственной целью которого является управление другими классами менеджера. Менеджер верхнего уровня, если хотите. Это упрощает замену реализаций в тестах без выбора полного решения DI, поскольку все действия и службы имеют одну и ту же ссылку на этот корень 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;
}
// ...
}
initialize
вызывается один раз, в Application.onCreate
, т.е. до того, как запущена какая-либо деятельность или служба. Теперь вот проблема: метод getInstance
иногда возвращается как null
- даже при вызове в том же потоке! Похоже, что это не проблема видимости; вместо этого статическая опорная ссылка одиночного элемента на уровне класса кажется фактически очищена сборщиком мусора. Может быть, я перехожу к выводам здесь, но может быть, это связано с тем, что сборщик мусора Android или механизм загрузки классов могут фактически выгружать классы, когда память становится недостаточной, и в этом случае единственная ссылка на экземпляр singleton исчезнет? Я не очень глубоко разбираюсь в модели памяти Java, но я полагаю, что этого не должно произойти, иначе этот общий способ реализации синглетонов не будет работать на любом JVM правильно?
Любая идея, почему это происходит именно?
PS: можно обойти это, сохранив вместо этого "глобальные" ссылки на экземпляр одного приложения. Это оказалось надежным, когда нужно держать объект в течение всего жизненного цикла приложения.
UPDATE
По-видимому, мое использование изменчивости вызвало некоторую путаницу. Мое намерение состояло в том, чтобы убедиться, что текущее состояние статической ссылки всегда видно все потокам доступа к его. Я должен сделать это, потому что я как пишу, так и читая эту ссылку из более чем одного потока: в обычном приложении запускается только в основном потоке приложения, но в прогоне контрольно-измерительного теста, где объекты заменяются на mocks, я пишу его из измерительной нитью и прочитать ее в потоке пользовательского интерфейса. Я мог бы также синхронизировать вызов с getInstance
, но это более дорого, так как требует потребовать блокировку объекта. См. Что представляет собой эффективный способ реализации одноэлементного шаблона в Java? для более подробного обсуждения этого вопроса.