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

145

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

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

hpique
источник
2
Связанный: stackoverflow.com/questions/1518154/…
Джош Ли

Ответы:

453

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

  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, как сообщалось выше.

Мои ожидания (которые , как представляется, поддерживаются числами 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, вероятно , возвращает значение 16MB, но я делать видим , что в моих (много позже) CyanogenMod версий пользователей Можно выбрать значение кучи до 12 МБ, что, вероятно, приведет к более низкому пределу кучи, и поэтому я бы посоветовал вам продолжить тестировать это maxMemory()значение даже для версий ОС до 2.0. Возможно, вам даже придется отказаться от запуска в маловероятном случае, если это значение установлено даже ниже 16 МБ, если вам нужно иметь больше, чем maxMemory()указано, разрешено.

деревенщина
источник
2
@Carl Привет, Карл, Спасибо за отличный пост. И я протестировал вариант, android: largeHeap = "true" на Galaxy S3 с JellyBean. Вот результат. [maxMemory: 256.0MB, класс памяти: 64MB] (maxMemory был 64MB без опции largeheap)
cmcromance
1
@Carl Haha Это большая честь! :)
cmcromance
1
Для HTC One X: max Память: 67108864 память Класс: 64
Кишор
1
LG Nexus 5 (4.4.3) (обычный): maxMemory: 201326592, класс памяти: 192 | LG Nexus 5 (4.4.3) (большая куча): maxMemory: 536870912, класс памяти: 192
ian.shaun.thomas
1
Очень красиво задокументировано. Пожалуйста, предоставьте это на Android. Я не могу найти такой подходящий документ для Android
Jimit Patel
20

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

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

hackbod
источник
13

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

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

Neil Traft
источник
Спасибо, Нил. Работает ли это, когда приложение работает с отключенной отладкой?
hpique
2
Я довольно новичок в этом, но я не думаю, что собственная куча такая же, как куча приложения (Dalvik), к которой относится вопрос. Собственная куча обеспечивает поддержку для растровых данных, которые выделяются собственным кодом, тогда как куча приложения содержит данные приложения Java. Мне было интересно узнать, что собственная куча засчитывается в ограничение кучи приложения, и после 3.0 выделение фактически происходит в куче приложения. Диана Хэкборн (hackbod) публикует сообщения на эту тему здесь: stackoverflow.com/questions/1945142/bitmaps-in-android Но даже для 3.0 в куче приложений есть и не «родные» данные.
Карл
1
Начиная с недавнего обновления Android (возможно, KitKat 4.4), растровые изображения находятся в куче JVM.
akauppi
13

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

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

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

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

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

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

long availableMemory=maxMemory-usedMemory;

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

String formattedMemorySize=Formatter.formatShortFileSize(context,memorySize); 
разработчик Android
источник
5

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

Runtime.getRuntime().maxMemory()

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

fhucho
источник
Поскольку документ для, по- getMemoryClassвидимому, подразумевает, что число может не совпадать с доступным размером кучи для вашего виртуального компьютера, а поскольку документ для getNativeHeapSize... молчалив, я действительно думаю, что Runtime.getRuntime().maxMemory()это лучший ответ.
СД
3

Некоторые операции выполняются быстрее, чем менеджер пространства кучи 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();
  }
}
Зон
источник
проверено хорошо. Можете ли вы разработать эти две вещи: availableMemoryPercentage и MIN_AVAILABLE_MEMORY_PERCENTAGE? некоторые объяснения?
Нур Хоссейн
1
AvailableMemoryPercentageв соответствии с формулой: сколько памяти устройства в настоящее время свободно. MIN_AVAILABLE_MEMORY_PERCENTAGEваш пользовательский параметр, порог, при котором вы начинаете ждать, пока сборщик мусора выполнит свою работу.
Зон
1

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

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

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

Tkraindesigns
источник
На упомянутом Nexus7-2013 getLargeMemoryClass () = 512.
akauppi
0

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

Стив Хейли
источник
Я имею в виду программно. Уточнил вопрос. Спасибо.
hpique
1
Я предлагаю вам посмотреть на этот пост одного из программистов платформы Android: stackoverflow.com/questions/2298208/…
Стив Хейли,
0
Runtime rt = Runtime.getRuntime();
rt.maxMemory()

значение b

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

значение МБ

qinqie
источник
Какой тип rt? Где это заявлено?
FindOut_Quran