Страницы

Поиск по вопросам

суббота, 30 ноября 2019 г.

Может ли Activity, обращающаяся к статическим полям быть причиной утечек памяти?

#android #memory_leaks


В приложении время от времени при логгировании возникают длиные цепочки, сообщающие
о работе GC, что, по моему мнению, говорит о некоторых утечках в памяти, так как если
бы все работало на ура, то не было бы огромного количества сообщений об аллокациях памяти. 

Стал вопрос оптимизации, ищу причины. Первая идея и мысль связана с логикой загрузки
приложения. Сначала я показываю активити-заглушку (пока загружаются все необходимые
данные), эти данные пишутся в статические поля некоторых классов для последующих операций,
то есть идет обращение к статическим ссылкам, которые не умирают. 

После вызывается finish, и я показываю активити, в которой идет все взаимодействие
юзера с приложением. Может обращение к статическим ссылкам быть одной из причин того,
что, например, активити не убивается при вызове finish и образуется утечка памяти?
    


Ответы

Ответ 1



Вообще, нет особой разницы в этом плане Java это чистая, или Android приложение. Хотя, я особо никогда не вникал в разницу между Dalvik и Hotspot jvm, но предполагаю, что GC работает примерно одинаково. Memory leak у вас может быть в случае, если static переменная в себе содержит линк на другой объект. Если вам уже не нужен этот объект, то, если вы забудете обнулить ссылку, он всё равно будет в памяти. Хотя, это сложно назвать именно утечкой, если брать в расчёт, что ссылка будет жить на протяжении жизни приложения. Формально, это будет перманентный синглтон. Может обращение к статическим ссылкам быть одной из причин того, что, например, активити не убивается при вызове finish и образуется утечка памяти? Вот это довольно интересный вопрос в контексте Android, так как в этой ОС жизненный цикл приложений весьма, хм, специфичный. Тут всё зависит, само собой, от того, что за переменные у вас статические. Так как в вопросе нету специфики, будет рассматривать абстрактные ситуации. Часто утечки бывают из-за context'а. Скажем, есть у вас: public class SomeClass extends SurfaceView { private static Context myContext; public MyInnerClass(Context context){ myContext = context; // здесь начинается печалька } } Даже если не останется ссылок на объекты SomeClass (вспомним, что если нет ссылок, то GC должен быть очистить память), скажем, когда Activity, которая показывала эту SurfaceView закрылась, статичная ссылка на Context (и любые другие статические переменные / константы в SomeClass останутся). Вы можете считать, что все они станут причиной утечкой памяти, так как нет возможности GC утилизировать Context. В контексте Android'а, могу сказать, что причиной утечек часто бывают как раз-такие ссылки на Activity, Context, View, Drawable, да и вообще любые объекты, которые содержат в себе ссылки на контейнер Activity или Context. Так же причиной могут быть нестатические внутренние классы (типо Runnable, которые могут содержать ссылки на Activity). Если же развить тему и уточнить вопрос: а вы уверены, что причина именно в static переменных? Handler с postDelay точно так же может стать причиной утечек. Можно почитать про возможные причины в блоге badoo. Или же в Android developers blog посмотреть пример утечек с Drawable. По сему, я бы сначала с помощью соответствующих утилит выявил, где конкретно утечки происходят, а уже от этого и отталкивался бы потом.

Ответ 2



Насколько я понимаю работу системы Android, то при вызове finish() активити вовсе принудительно не уничтожается (не удаляется из памяти), она только помечается "не активной" - происходит вызов методов завершения (onPause(), onStop(), onDestroy()) и манипуляции со списком переходов. На передний план отображается активити с вершины стека. Сама же "финишированная" активити существует в памяти до того момента, пока GC не решит провести очистку по каким то своим соображениям. Соответственно, если активити имеет статические ссылки (которые существуют до окончания работы приложения), то уничтожена сборщиком мусора она не будет, даже не смотря на то, что и помечена "не активной", так как есть жесткая ссылка - на лицо явная утечка памяти. Вообще, Android Studio в последней версии (1.5 на сегодняшний день) включает Memory Monitor для поиска утечек (инструменты HPROF Viewer и Allocation Tracker). Разумно будет воспользоваться ими и посмотреть, что же происходит. Здесь вы можете принудительно вызвать GC (очистку памяти) и проследить за судьбой своей активити и что ее держит в этом бренном мире. Очень часто там ждут неожиданные сюрпризы. Смотрите так же статью по утечкам памяти на офф.сайте

Ответ 3



Не нужно хранить глобальные переменные в активити. Если Вам нужно сохранить переменные на весь жизненный цикл приложения, то есть 2 варианта, которые позволяют это сделать: Первый - это создать свой класс наследник Application, где определить эти глобальные переменные и написать гетеры и сеттеры для них. public class MyApplication extends Application { private MyCustomObject myGlobalField; @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); } @Override public void onCreate() { super.onCreate(); } @Override public void onLowMemory() { super.onLowMemory(); } @Override public void onTerminate() { super.onTerminate(); } public MyCustomObject getMyGlobalField() { return this.myGlobalField; } public void setMyGlobalField(MyCustomObject myGlobalField) this.myGlobalField = myGlobalField; } } Не забудьте указать Ваш класс приложения в манифесте: Затем, в Ваших активити вы получаете доступ к этим полям: MyApplication myApplication = (MyApplication)getApplicationContext(); MyCustomObject myGlobalField = myApplication.getMyGlobalField(); Второй вариант - это хранить все глобальные данные в SharedPreferences. Но, это вариант не подходит, если вы хотите сохранить свои custom Object, а не обычные примитивы.

Ответ 4



Хотя статические переменные и нарушают принцип взаимодействия между Activity (правильно передавать данные через Intent, но придется научить все свои объекты сериализоваться), но утечек памяти это вызывать не должно. Надо искать в других местах. Например, стандартная утилита из Android SDK 'android-sdk\tools\monitor.bat' имеет минимальный функционал для отслеживания выделения памяти.

Комментариев нет:

Отправить комментарий