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

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

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

Ответ 1

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

  • Сколько кучи может использовать мое приложение до того, как будет запущена жесткая ошибка? И

  • Сколько кучи должно использовать мое приложение, учитывая ограничения версии ОС 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 для Android maxMemory() обычно возвращается примерно столько же мегабайт, как указано в getMemoryClass() (т.е. примерно в миллион раз больше последнего значения).

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

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

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

  • G1:
    • С размером кучи VM установлено значение 16 МБ:
      • maxMemory: 16777216
      • getMemoryClass: 16
    • С размером кучи VM установлено значение 24 МБ:
      • maxMemory: 25165824
      • getMemoryClass: 16
  • Мото-дроид:
    • С размером кучи VM установлено значение 24 МБ:
      • maxMemory: 25165824
      • getMemoryClass: 24
    • С размером кучи VM установлено значение 16 МБ:
      • maxMemory: 16777216
      • getMemoryClass: 24
  • Nexus One:
    • С размером кучи VM установлено значение 32 МБ:
      • maxMemory: 33554432
      • getMemoryClass: 32
    • С размером кучи VM установлено значение 24 МБ:
      • maxMemory: 25165824
      • getMemoryClass: 32
  • Viewsonic GTab:
    • С размером кучи VM установлено значение 32:
      • maxMemory: 33554432
      • getMemoryClass: 32
    • С размером кучи VM установлено значение 64:
      • maxMemory: 67108864
      • getMemoryClass: 32

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

Для этого устройства приведены следующие результаты:

  • Novo7
    • maxMemory: 62914560
    • getMemoryClass: 60

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

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

И (за комментарий akauppi):

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

В комментарии от cmcromance:

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

И (за каждый комментарий):

  • 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 у нас есть некоторые значения sampleHeap образца, как описано выше.

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

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

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

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

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

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

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

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

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

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

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

Ответ 2

Официальным API является:

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

Ответ 3

Debug.getNativeHeapSize() будет делать трюк, я должен подумать. Он был там с 1.0, однако.

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

Ответ 4

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

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

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

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

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

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

long availableMemory=maxMemory-usedMemory;

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

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

Ответ 5

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

Runtime.getRuntime().maxMemory()

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

Ответ 6

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

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

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

Ответ 7

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

Ответ 8

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

Значение: b

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

значение - MB

Ответ 9

Некоторые операции выполняются быстрее, чем диспетчер пространства кучи 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();
  }
}