Определить размер кучи приложения в Android

Как вы программно определяете размер кучи приложения, доступного для приложения Android?

Я слышал, что есть функция, которая делает это в более поздних версиях SDK. В любом случае, я ищу решение, которое работает на 1,5 и выше.

9 ответов

Решение

Есть два способа подумать о вашей фразе "доступный размер кучи приложения":

  1. Сколько кучи может использовать мое приложение до появления серьезной ошибки? А также

  2. Сколько кучи должно использовать мое приложение, учитывая ограничения версии ОС Android и аппаратное обеспечение устройства пользователя?

Существует другой метод определения каждого из вышеперечисленных.

Для пункта 1 выше: maxMemory()

который может быть вызван (например, в вашей основной деятельности onCreate() метод) следующим образом:

Runtime rt = Runtime.getRuntime();
long maxMemory = rt.maxMemory();
Log.v("onCreate", "maxMemory:" + Long.toString(maxMemory));

Этот метод сообщает, сколько всего байтов кучи разрешено использовать вашему приложению.

Для пункта 2 выше: getMemoryClass()

который может быть вызван следующим образом:

ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
int memoryClass = am.getMemoryClass();
Log.v("onCreate", "memoryClass:" + Integer.toString(memoryClass));

Этот метод сообщает вам, приблизительно, сколько мегабайт кучи должно использовать ваше приложение, если оно хочет должным образом уважать ограничения существующего устройства и права других приложений на запуск без повторного принуждения к onStop() / onResume() цикл, поскольку они грубо вымываются из памяти, в то время как ваше приложение для слонов принимает ванну в джакузи Android.

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

Для стоковой версии Android maxMemory() обычно возвращает примерно столько же мегабайт, сколько указано в getMemoryClass() (т.е. примерно в миллион раз больше последнего значения).

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

ПРИМЕЧАНИЕ: ВНИМАНИЕ, ЧТО УСТАНАВЛИВАЕТ ДАННОЕ ЗНАЧЕНИЕ ВРУЧНУЮ, МОЖЕТ ПОПАДАТЬ В ВАШУ СИСТЕМУ, ОСОБЕННО, если вы выберете меньшее значение, чем обычно для вашего устройства.

Вот мои результаты теста, показывающие значения, возвращенные maxMemory() а также getMemoryClass() для четырех разных устройств, работающих под управлением CyanogenMod, для каждого из них используются два разных (установленных вручную) значения кучи:

  • G1:
    • Если размер кучи виртуальной машины установлен в 16 МБ:
      • maxMemory: 16777216
      • getMemoryClass: 16
    • Если размер кучи виртуальной машины установлен на 24 МБ:
      • maxMemory: 25165824
      • getMemoryClass: 16
  • Мото Дроид:
    • Если размер кучи виртуальной машины установлен на 24 МБ:
      • maxMemory: 25165824
      • getMemoryClass: 24
    • Если размер кучи виртуальной машины установлен в 16 МБ:
      • maxMemory: 16777216
      • getMemoryClass: 24
  • Nexus One:
    • Если размер кучи виртуальной машины равен 32 МБ:
      • maxMemory: 33554432
      • getMemoryClass: 32
    • Если размер кучи виртуальной машины установлен на 24 МБ:
      • maxMemory: 25165824
      • getMemoryClass: 32
  • Viewsonic GTab:
    • Если размер кучи виртуальной машины установлен на 32:
      • maxMemory: 33554432
      • getMemoryClass: 32
    • Если размер кучи виртуальной машины установлен на 64:
      • maxMemory: 67108864
      • getMemoryClass: 32

В дополнение к вышесказанному, я тестировал планшет Novo7 Paladin с Ice Cream Sandwich. По сути, это была стандартная версия ICS, за исключением того, что я проверил планшет через простой процесс, который не заменяет всю ОС, и, в частности, не предоставляет интерфейс, который позволял бы настраивать размер кучи вручную.

Для этого устройства вот результаты:

  • Novo7
    • maxMemory: 62914560
    • getMemoryClass: 60

Также (за Кишор в комментарии ниже):

  • HTC One X
    • maxMemory: 67108864
    • getMemoryClass: 64

И (согласно комментарию Акауппи):

  • Samsung Galaxy Core Plus
    • maxMemory: (не указано в комментарии)
    • getMemoryClass: 48
    • largeMemoryClass: 128

За комментарий от cmcromance:

  • Galaxy S3 (Jelly Bean) большая куча
    • maxMemory: 268435456
    • getMemoryClass: 64

И (комментарии tencent):

  • LG Nexus 5 (4.4.3) нормальный
    • maxMemory: 201326592
    • getMemoryClass: 192
  • LG Nexus 5 (4.4.3) большая куча
    • maxMemory: 536870912
    • getMemoryClass: 192
  • Galaxy Nexus (4.3) нормальный
    • maxMemory: 100663296
    • getMemoryClass: 96
  • Galaxy Nexus (4.3) большая куча
    • maxMemory: 268435456
    • getMemoryClass: 96
  • Galaxy S4 Play Store Edition (4.4.2) обычный
    • maxMemory: 201326592
    • getMemoryClass: 192
  • Galaxy S4 Play Store Edition (4.4.2) большая куча
    • maxMemory: 536870912
    • getMemoryClass: 192

Другие устройства

  • Huawei Nexus 6P (6.0.1) нормальный
    • maxMemory: 201326592
    • getMemoryClass: 192

Я не тестировал эти два метода, используя специальный параметр android:largeHeap="true", доступный со времен Honeycomb, но благодаря cmcromance и tencent у нас есть некоторые примерные значения largeHeap, как сообщалось выше.

Я ожидаю (что, как представляется, подтверждается большими числами выше), что этот параметр будет иметь эффект, аналогичный настройке кучи вручную через рутированную ОС - то есть он повысит значение maxMemory() оставляя getMemoryClass() в одиночестве. Есть еще один метод, getLargeMemoryClass(), который указывает, сколько памяти разрешено для приложения, использующего параметр largeHeap. В документации для getLargeMemoryClass () говорится, что "большинству приложений не требуется такой объем памяти, и вместо этого они должны оставаться с ограничением getMemoryClass()".

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

Обратите внимание, что класс памяти, очевидно, не должен быть кратным 8 МБ.

Из вышесказанного видно, что getMemoryClass() результат неизменен для данной конфигурации устройства / ОС, в то время как значение maxMemory() изменяется, когда куча задается пользователем по-разному.

Мой собственный практический опыт заключается в том, что на G1 (который имеет класс памяти 16), если я вручную выберу 24 МБ в качестве размера кучи, я могу работать без ошибок, даже если использование моей памяти может дойти до 20 МБ (предположительно, это может доходит до 24 МБ, хотя я не пробовал это). Но другие приложения такого же размера могут быть выгружены из памяти из-за скудности моего собственного приложения. И, наоборот, мое приложение может быть выгружено из памяти, если пользователь выведет эти другие приложения с высоким уровнем обслуживания.

Таким образом, вы не можете превысить объем памяти, указанный maxMemory(), И вы должны попытаться остаться в пределах, указанных getMemoryClass(), Одним из способов сделать это, если ничего не помогает, может быть ограничение функциональности таких устройств таким образом, чтобы сохранить память.

Наконец, если вы планируете пересмотреть количество мегабайт, указанное в getMemoryClass() Я бы посоветовал вам долго и усердно работать над сохранением и восстановлением состояния вашего приложения, чтобы пользовательский опыт практически не прерывался, если onStop() / onResume() цикл происходит.

В моем случае из соображений производительности я ограничиваю свое приложение устройствами, работающими на 2.2 и выше, а это означает, что почти все устройства, на которых работает мое приложение, будут иметь класс памяти 24 или выше. Так что я могу спроектировать так, чтобы занимать до 20 МБ кучи, и чувствую себя довольно уверенно, что мое приложение будет хорошо играть с другими приложениями, которые пользователь может одновременно запускать.

Но всегда будет несколько пользователей с правами root, которые загрузили версию Android 2.2 или выше на старое устройство (например, G1). Когда вы сталкиваетесь с такой конфигурацией, в идеале вам следует сократить использование памяти, даже если maxMemory() говорит вам, что вы можете пойти гораздо выше, чем 16 МБ, что getMemoryClass() говорит вам, что вы должны быть нацелены. И если вы не можете надежно гарантировать, что ваше приложение будет жить в рамках этого бюджета, то по крайней мере убедитесь, что onStop() / onResume() работает без проблем.

getMemoryClass() как указано выше Дианой Хэкборн (hackbod), она доступна только для уровня API 5 (Android 2.0), и поэтому, как она советует, можно предположить, что физическое оборудование любого устройства, работающего под управлением более ранней версии ОС, разработано для оптимальной поддержки приложений, занимающих пространство кучи не более 16 МБ.

В отличие от maxMemory() Согласно документации, доступен вплоть до уровня API 1. maxMemory() в версии до 2.0, вероятно, вернет значение 16 МБ, но я вижу, что в моих (гораздо более поздних) версиях CyanogenMod пользователь может выбрать значение кучи, равное 12 МБ, что, вероятно, приведет к более низкому пределу кучи, и поэтому я хотел бы предложить вам продолжить тестирование maxMemory() значение даже для версий ОС до 2.0. Возможно, вам даже придется отказаться от запуска в маловероятном случае, если это значение установлено даже ниже, чем 16 МБ, если вам нужно иметь больше, чем maxMemory() указывает разрешено.

Официальный API это:

Это было введено в 2.0, где появились большие устройства памяти. Можно предположить, что устройства под управлением предыдущих версий ОС используют исходный класс памяти (16).

Вот как вы это делаете:

Получение максимального размера кучи, который может использовать приложение:

Runtime runtime = Runtime.getRuntime();
long maxMemory=runtime.maxMemory();

Как узнать, сколько кучи в настоящее время использует ваше приложение:

long usedMemory=runtime.totalMemory() - runtime.freeMemory();

Получение того, сколько кучи может теперь использовать ваше приложение (доступная память):

long availableMemory=maxMemory-usedMemory;

И, чтобы красиво отформатировать каждый из них, вы можете использовать:

String formattedMemorySize=Formatter.formatShortFileSize(context,memorySize); 

Debug.getNativeHeapSize() Я подумаю. Это было там с 1.0, хотя.

Debug В классе есть много отличных методов для отслеживания выделения ресурсов и других проблем с производительностью. Кроме того, если вам нужно обнаружить нехватку памяти, проверьте Activity.onLowMemory(),

Это возвращает максимальный размер кучи в байтах:

Runtime.getRuntime().maxMemory()

Я использовал ActivityManager.getMemoryClass(), но в CyanogenMod 7 (я не проверял его в других местах) он возвращает неправильное значение, если пользователь устанавливает размер кучи вручную.

Некоторые операции выполняются быстрее, чем менеджер пространства кучи Java. Задержка операций на некоторое время может освободить место в памяти. Вы можете использовать этот метод, чтобы избежать ошибки размера кучи:

waitForGarbageCollector(new Runnable() {
  @Override
  public void run() {

    // Your operations.
  }
});

/**
 * Measure used memory and give garbage collector time to free up some
 * space.
 *
 * @param callback Callback operations to be done when memory is free.
 */
public static void waitForGarbageCollector(final Runnable callback) {

  Runtime runtime;
  long maxMemory;
  long usedMemory;
  double availableMemoryPercentage = 1.0;
  final double MIN_AVAILABLE_MEMORY_PERCENTAGE = 0.1;
  final int DELAY_TIME = 5 * 1000;

  runtime =
    Runtime.getRuntime();

  maxMemory =
    runtime.maxMemory();

  usedMemory =
    runtime.totalMemory() -
    runtime.freeMemory();

  availableMemoryPercentage =
    1 -
    (double) usedMemory /
    maxMemory;

  if (availableMemoryPercentage < MIN_AVAILABLE_MEMORY_PERCENTAGE) {

    try {
      Thread.sleep(DELAY_TIME);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }

    waitForGarbageCollector(
      callback);
  } else {

    // Memory resources are availavle, go to next operation:

    callback.run();
  }
}

Asus Nexus 7 (2013) 32Gig: getMemoryClass()=192 maxMemory()=201326592

Я сделал ошибку, прототипировав свою игру на Nexus 7, а затем обнаружил, что почти сразу же не хватило памяти на универсальном планшете моей жены 4.04 (memoryclass 48, maxmemory 50331648)

Мне нужно будет реструктурировать свой проект, чтобы загружать меньше ресурсов, когда я определяю, что класс памяти мал.
Есть ли способ в Java, чтобы увидеть текущий размер кучи? (Я вижу это ясно в logCat при отладке, но мне бы хотелось, чтобы это можно было увидеть в коде для адаптации, например, если currentheap>(maxmemory/2) выгружает высококачественные растровые изображения загружает низкое качество

Runtime rt = Runtime.getRuntime();
rt.maxMemory()

значение b

ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
am.getMemoryClass()

значение МБ

Вы имеете в виду программно или просто во время разработки и отладки? Если последнее, вы можете увидеть эту информацию с точки зрения DDMS в Eclipse. Когда ваш эмулятор (возможно, даже физический телефон, который подключен) работает, он выведет список активных процессов в окне слева. Вы можете выбрать его, и есть возможность отслеживать распределение кучи.

Другие вопросы по тегам