Определить размер кучи приложения в Android
Как вы программно определяете размер кучи приложения, доступного для приложения Android?
Я слышал, что есть функция, которая делает это в более поздних версиях SDK. В любом случае, я ищу решение, которое работает на 1,5 и выше.
9 ответов
Есть два способа подумать о вашей фразе "доступный размер кучи приложения":
Сколько кучи может использовать мое приложение до появления серьезной ошибки? А также
Сколько кучи должно использовать мое приложение, учитывая ограничения версии ОС 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
- Если размер кучи виртуальной машины установлен в 16 МБ:
- Мото Дроид:
- Если размер кучи виртуальной машины установлен на 24 МБ:
- maxMemory: 25165824
- getMemoryClass: 24
- Если размер кучи виртуальной машины установлен в 16 МБ:
- maxMemory: 16777216
- getMemoryClass: 24
- Если размер кучи виртуальной машины установлен на 24 МБ:
- Nexus One:
- Если размер кучи виртуальной машины равен 32 МБ:
- maxMemory: 33554432
- getMemoryClass: 32
- Если размер кучи виртуальной машины установлен на 24 МБ:
- maxMemory: 25165824
- getMemoryClass: 32
- Если размер кучи виртуальной машины равен 32 МБ:
- Viewsonic GTab:
- Если размер кучи виртуальной машины установлен на 32:
- maxMemory: 33554432
- getMemoryClass: 32
- Если размер кучи виртуальной машины установлен на 64:
- maxMemory: 67108864
- getMemoryClass: 32
- Если размер кучи виртуальной машины установлен на 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. Когда ваш эмулятор (возможно, даже физический телефон, который подключен) работает, он выведет список активных процессов в окне слева. Вы можете выбрать его, и есть возможность отслеживать распределение кучи.