Большой кусок памяти, а не сбор мусора - программирование
Подтвердить что ты не робот

Большой кусок памяти, а не сбор мусора

При поиске утечки памяти в моем приложении я преследовал поведение, которое я не могу понять. Я выделяю большой блок памяти, но он не получает сбор мусора, приводящий к 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();
        }
    }

}
4b9b3361

Ответ 1

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

Ответ 2

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

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

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

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