2013-06-19 2 views
16

В поисках утери памяти в моем приложении я преследовал поведение, которое я не могу понять. Я выделяю большой блок памяти, но он не получает сбор мусора, приводящий к OOM, если только я не укажу null ссылку в onDestroy.Большой кусок памяти, а не сборщик мусора

В этом примере у меня есть две почти идентичные действия, которые переключаются между собой. Оба имеют одну кнопку. При нажатии кнопки MainActivity начинается OOMActivity и OOMActivity возвращается, вызывая finish(). После нажатия кнопок несколько раз, Android выдает OOMException.

Если я добавлю onDestroy к OOMActivity и явную нулевую ссылку на кусок памяти, я могу видеть в журнале, что память правильно освобождена.

Почему память не освобождается автоматически без обнуления?

MainActivity:

package com.example.oom; 

import android.app.Activity; 
import android.content.Intent; 
import android.os.Bundle; 
import android.view.View; 
import android.view.View.OnClickListener; 
import android.widget.Button; 

public class MainActivity extends Activity implements OnClickListener { 

    private int buttonId; 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     System.gc(); 
     Button OOMButton = new Button(this); 
     OOMButton.setText("OOM"); 
     buttonId = OOMButton.getId(); 

     setContentView(OOMButton); 
     OOMButton.setOnClickListener(this); 
    } 

    @Override 
    public void onClick(View v) { 
     if (v.getId() == buttonId) { 
      Intent leakIntent = new Intent(this, OOMActivity.class); 
      startActivity(leakIntent); 
     } 
    } 

} 

OOMActivity:

public class OOMActivity extends Activity implements OnClickListener { 

    private static final int WASTE_SIZE = 20000000; 
    private byte[] waste; 
    private int buttonId; 

    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 

     Button BackButton = new Button(this); 
     BackButton.setText("Back"); 
     buttonId = BackButton.getId(); 

     setContentView(BackButton); 
     BackButton.setOnClickListener(this); 

     waste = new byte[WASTE_SIZE]; 

    } 

    public void onClick(View view) { 
     if (view.getId() == buttonId) { 
      finish(); 
     } 
    } 

} 
+0

отличный пример, большой вопрос! – WarrenFaith

+0

Прочтите этот чат. Мы [обсуждали это здесь] (http://chat.stackoverflow.com/transcript/message/10084186#10084186) – Reno

+2

Возможно, вы тестируете это на устройстве с предварительной сотой. Сборщик мусора на сотке будет достаточно агрессивен, чтобы освободить объект «отходов». Протестировано на 4.1.2, 4.2.2, 2.3.5, 2.3.7. Вызов System.gc() перед «waste = new byte [WASTE_SIZE]» позволяет избежать проблемы на устройствах с предварительной сотой. – pellucide

ответ

1

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

1

Несколько вещей:

1) Вы не можете судить о том или не просочилась ваша активность, просто наблюдая журналы GC; каждая реализация JVM может выбирать, когда собирать мусор, даже если ничто не ссылается на них. Обратите внимание, что требуется сбор мусора, прежде чем он выведет ошибку OOM ... но если у нее достаточно памяти, она может сохранить 10 ваших действий в памяти, а затем собрать их все сразу.

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

3) Некоторые JVM оптимизируются во время выполнения, так что кусок памяти, который никогда не записывается или не используется, на самом деле никогда не выделяется в физической памяти. Возможно, по этой причине ваш тест недействителен в новых версиях Android. Чтобы обойти это, вы можете добавить цикл, который устанавливает некоторые значения в массиве случайным значениям, а затем другой цикл где-то еще в коде, который их читает; таким образом, JVM вынужден выделять память.

Смежные вопросы