OutOfMemoryException в эмуляторе

Я действительно не знаю, как справиться с этой ситуацией. Внезапно я получаю исключение OutOfMemoryException для определенного действия, и он тот же каждый раз, следуя тому же пути в игре, которую я разрабатываю. Я использую много Drawables в своей игре. Я где-то читал, что вы часто должны выполнять Bitmap.recycle(), чтобы освободить память. Но я почти никогда не использую Bitmaps напрямую и Drawabels, которые я использую, мне нужно, чтобы они были на экране.

Это, вероятно, не помогает, что я использую Facebook и Parse SDK.

Сейчас я развиваюсь на своем телефоне и в эмуляторе. В эмуляторе размер кучи не может превышать 30 МБ. Должен ли я увеличить это число или оставить его в покое и попытаться уменьшить потребление памяти в моем приложении?

То, что я предполагал до недавнего времени, заключалось в том, что как только я закончу деятельность и перейду в другую, что все будет собирать мусор, и я начинаю свежие Пример кода:

private void startGamesActivity() {    
    Intent intent = new Intent(this, SS3GamesActivity.class);
    startActivity(intent);      
finish();   
}

Это не так? Позвольте мне подробнее остановиться. У меня есть Activity с двумя ListViews возможных игроков в Facebook. Исключение OutOfMemoryException происходит, когда я нажимаю на один из них, чтобы начать новую активность, которая должна быть меньше потребляемой памяти, чем раньше! Вот почему я не понимаю, почему OutOfMemoryException даже случается! Должно быть много памяти, оставшейся после того, как два ListViews получат сбор мусора!

Итак, теперь я проанализировал свое потребление кучи. Я также загрузил Eclipse Memory Analyzer. Самый большой кусок памяти, который используется, является 1-байтовый массив (byte [], boolean []), который имеет счет 702, общий размер 20 096 МБ, где самый большой из них составляет 2640 МБ и 29 314 МБ. Это поражает меня, как много! Это даже не очень сильно влияет на приложение. Но, видя, что в эмуляторе размер кучи не увеличивается более 30 МБ, он сталкивается с большим потенциалом.

Используя Eclipse Memory Analyzer, он также дает мне двух возможных виновников утечки памяти.

Проблема Подозреваемый 1 Один экземпляр "android.graphics.Bitmap", загруженный ", занимает 6.543.416 (28,00%) байтов. Память накапливается в одном экземпляре" byte [] ", загружаемом" ".

Проблема Подозреваемый 2 Класс "android.content.res.Resources", загруженный ", занимает 5,212,968 (22,31%) байт. Память накапливается в одном экземпляре" java.lang.Object [] ", загруженном" ".

Результат не так прост, как я думал. Я думаю, что возможный подозреваемый, судя по именам классов, - это мой LruCache. Я использую это, чтобы заполнить Facebook Thumbnails возможных фотографий игрока, чтобы не загружать их из Интернета каждый раз, когда пользователь прокручивает список ListView. Я не уверен, почему он уже настолько велик в начале моего приложения. Я уже пытался сделать его меньше, но не повезло.

Это в onCreate моей MainActivity:

// caching system
final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);      
final int cacheSize = maxMemory / 16; // used to be 8, didnt help though

   mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
        @Override
        protected int sizeOf(String key, Bitmap bitmap) {
            // The cache size will be measured in kilobytes rather than
            // number of items.
            return bitmap.getByteCount() / 1024;
        }
    };  

Я также проверил второго возможного подозреваемого, ressources, и я действительно не мог этого сделать. Кажется, что все мои объекты макета, которые я использую, и я не могу использовать меньше того, что я делаю прямо сейчас.

Кроме того, пока мы находимся в этом вопросе, поможет ли что-то вроде "onDestroy"?

public class ExampleActivity extends Activity {

    HashMap<String, String> meMap=new HashMap<String, String>();
    static ArrayList<String> meArray = new ArrayList<String>();

    protected void onDestroy() {        
        super.onDestroy();
        meMap.clear();
        meMap = null;

        meArray.clear();
        meArray = null;
    }

}

Потому что я не видел много разницы в потреблении памяти, поэтому я предполагаю, что gc делает это автоматически.

Я с удовольствием принимаю все возможные предложения по моей проблеме. Я даже могу предоставить дополнительную информацию, но на данный момент я даже не знаю, что.

StackTrace в зеленом цвете:

05-06 07:37:12.373: I/dalvikvm(529): Wrote stack traces to '/data/anr/traces.txt'
05-06 07:37:12.523: E/dalvikvm-heap(529): Out of memory on a 3601936-byte allocation.
05-06 07:37:12.523: I/dalvikvm(529): "main" prio=5 tid=1 RUNNABLE
05-06 07:37:12.533: I/dalvikvm(529):   | group="main" sCount=0 dsCount=0 obj=0x409c1460 self=0x12810
05-06 07:37:12.533: I/dalvikvm(529):   | sysTid=529 nice=0 sched=0/0 cgrp=default handle=1074082952
05-06 07:37:12.533: I/dalvikvm(529):   | schedstat=( 8842421920 6327029011 3892 ) utm=778 stm=106 core=0
05-06 07:37:12.533: I/dalvikvm(529):   at android.graphics.Bitmap.nativeCreate(Native Method)
05-06 07:37:12.533: I/dalvikvm(529):   at android.graphics.Bitmap.createBitmap(Bitmap.java:605)
05-06 07:37:12.533: I/dalvikvm(529):   at android.graphics.Bitmap.createBitmap(Bitmap.java:551)
05-06 07:37:12.533: I/dalvikvm(529):   at android.graphics.Bitmap.createScaledBitmap(Bitmap.java:437)
05-06 07:37:12.533: I/dalvikvm(529):   at android.graphics.BitmapFactory.finishDecode(BitmapFactory.java:524)
05-06 07:37:12.533: I/dalvikvm(529):   at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:499)
05-06 07:37:12.533: I/dalvikvm(529):   at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:351)
05-06 07:37:12.533: I/dalvikvm(529):   at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:773)
05-06 07:37:12.533: I/dalvikvm(529):   at android.content.res.Resources.loadDrawable(Resources.java:1935)
05-06 07:37:12.533: I/dalvikvm(529):   at android.content.res.TypedArray.getDrawable(TypedArray.java:601)
05-06 07:37:12.533: I/dalvikvm(529):   at android.widget.ImageView.<init>(ImageView.java:119)
05-06 07:37:12.533: I/dalvikvm(529):   at android.widget.ImageView.<init>(ImageView.java:109)
05-06 07:37:12.533: I/dalvikvm(529):   at java.lang.reflect.Constructor.constructNative(Native Method)
05-06 07:37:12.533: I/dalvikvm(529):   at java.lang.reflect.Constructor.newInstance(Constructor.java:417)
05-06 07:37:12.533: I/dalvikvm(529):   at android.view.LayoutInflater.createView(LayoutInflater.java:586)
05-06 07:37:12.533: I/dalvikvm(529):   at com.android.internal.policy.impl.PhoneLayoutInflater.onCreateView(PhoneLayoutInflater.java:56)
05-06 07:37:12.533: I/dalvikvm(529):   at android.view.LayoutInflater.onCreateView(LayoutInflater.java:653)
05-06 07:37:12.543: I/dalvikvm(529):   at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:678)
05-06 07:37:12.543: I/dalvikvm(529):   at android.view.LayoutInflater.rInflate(LayoutInflater.java:739)
05-06 07:37:12.543: I/dalvikvm(529):   at android.view.LayoutInflater.rInflate(LayoutInflater.java:742)
05-06 07:37:12.543: I/dalvikvm(529):   at android.view.LayoutInflater.inflate(LayoutInflater.java:489)
05-06 07:37:12.543: I/dalvikvm(529):   at android.view.LayoutInflater.inflate(LayoutInflater.java:396)
05-06 07:37:12.543: I/dalvikvm(529):   at android.view.LayoutInflater.inflate(LayoutInflater.java:352)
05-06 07:37:12.543: I/dalvikvm(529):   at com.android.internal.policy.impl.PhoneWindow.setContentView(PhoneWindow.java:251)
05-06 07:37:12.543: I/dalvikvm(529):   at android.app.Activity.setContentView(Activity.java:1835)
05-06 07:37:12.543: I/dalvikvm(529):   at com.quizdom.SS7ChooseTopicsActivity.onCreate(SS7ChooseTopicsActivity.java:36)
05-06 07:37:12.543: I/dalvikvm(529):   at android.app.Activity.performCreate(Activity.java:4465)
05-06 07:37:12.543: I/dalvikvm(529):   at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1049)
05-06 07:37:12.543: I/dalvikvm(529):   at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1920)
05-06 07:37:12.543: I/dalvikvm(529):   at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1981)
05-06 07:37:12.543: I/dalvikvm(529):   at android.app.ActivityThread.access$600(ActivityThread.java:123)
05-06 07:37:12.543: I/dalvikvm(529):   at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1147)
05-06 07:37:12.543: I/dalvikvm(529):   at android.os.Handler.dispatchMessage(Handler.java:99)
05-06 07:37:12.543: I/dalvikvm(529):   at android.os.Looper.loop(Looper.java:137)
05-06 07:37:12.543: I/dalvikvm(529):   at android.app.ActivityThread.main(ActivityThread.java:4424)
05-06 07:37:12.543: I/dalvikvm(529):   at java.lang.reflect.Method.invokeNative(Native Method)
05-06 07:37:12.543: I/dalvikvm(529):   at java.lang.reflect.Method.invoke(Method.java:511)
05-06 07:37:12.543: I/dalvikvm(529):   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
05-06 07:37:12.543: I/dalvikvm(529):   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
05-06 07:37:12.543: I/dalvikvm(529):   at dalvik.system.NativeStart.main(Native Method)

Ответ 1

Очевидно, я не видел растровые изображения, которые вы загружаете, но я предполагаю, что они намного больше, чем они должны быть для пространства, в котором вы их показываете. Если вы загружаете, например, изображение 3000 x 2000, а затем назначаете его как источник ImageView, который только 300 x 200, исходный размер растрового изображения все еще сохраняется в памяти и просто отображается меньше. Важно, чтобы при загрузке изображения из Интернета вы изменяли размер полученного растрового изображения до размера экрана, прежде чем устанавливать его в качестве источника ImageView. Не делать этого будет потреблять огромные куски памяти.

Это довольно много кода, поскольку вам нужно защищать от множества различных ошибок в ходе этого процесса. Самое простое решение, которое я могу вам предложить, это попробовать библиотеку Picasso. Вы можете передать ему URL-адрес, который вы хотите загрузить, с кодом, подобным показанному ниже, и он загрузит его на фоновый поток, изменит его размер и поместит в ImageView, когда он будет готов. Он также будет кэшировать измененную версию, чтобы, если вам нужно загрузить одно и то же изображение, она использует кешированную версию, если она подходит для ImageView, для которой вы ее загружаете. Это означает, что вы также не тратите сетевой трафик. Если вам нужно сделать некоторые преобразования к изображению до его размещения в ImageView, вы можете сделать это, предоставив объект Transform. Вы также можете установить образ заполнителя, который вы должны иметь в своих папках drawable, и отобразит это до тех пор, пока не будет загружено фактическое изображение.

Picasso.with(context)
       .load("http://i.imgur.com/DvpvklR.png")
       .into(imageView);

Ответ 2

Основные шаги

В соответствии с этим ответом в этом случае вам нужно выполнить следующие основные шаги:

  • В Android 3.0+ используйте inBitmap в BitmapOptions, который вы передаете BitmapFactory, чтобы повторно использовать существующую память, а не выделять новую память
  • recycle() ваши объекты Bitmap, когда вы закончите с ними
  • Будьте осторожны с распределением памяти, поскольку сборщик мусора Android не уплотняется, поэтому в конечном итоге вы не сможете снова выделить большие блоки памяти.
  • Используйте MAT, чтобы узнать, не утечка памяти где-то, что способствует вашей проблеме.

Некоторые сообщения, которые могут быть полезны

Смотрите это сообщение о Управление растровой памятью, а также это о Загрузка больших растровых изображений. Это может помочь вам.

Также вы должны обработать свою рекламу на своем ListView. См. Этот пост о Обработка ListView Recycling.

Надеюсь, это поможет вам.

Ответ 3

Я думаю, проблема в том, что вы протекаете текущий Activity. Вы передаете this в качестве первого параметра, который в этом случае является Context. Итак, вы используете текущую деятельность как контекст, а следующая вещь, которую вы делаете, это закончить. Активность закрывается, но ее память просачивается, потому что вы передали ссылку на намерение.

Попробуйте следующее:

Intent intent = new Intent(getApplicationContext(), SS3GamesActivity.class);