Відповіді:
Є два способи подумати над своєю фразою "розмір купи програми":
Скільки купи може використовувати моє додаток до появи сильної помилки? І
Скільки купи повинен використовувати мій додаток, враховуючи обмеження версії ОС 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" / "Продуктивність" / "розмір купівлі VM".
ПРИМІТКА. Будьте уважні, що НАСТРОЮВАННЯ ЦІЙ ЦІННОСТІ МОЖЛИВИ ЗМЕСТИТИ ВАШУ СИСТЕМУ, ОСОБЛИВО, якщо ви виберете менше значення, ніж це нормально для вашого пристрою.
Ось мої результати тестування, які показують значення, повернені maxMemory()
і getMemoryClass()
для чотирьох різних пристроїв, на яких працює CyanogenMod, використовуючи два різні (вручну встановлені) значення купи для кожного:
На додаток до вищезазначеного, я протестував на планшеті Paladin Novo7, де працює сендвіч з морозивом. По суті це була біржова версія ICS, за винятком того, що я вкоренив планшет через простий процес, який не замінює всю ОС, і, зокрема, не забезпечує інтерфейс, який би дозволяв регулювати розмір купи вручну.
Для цього пристрою наведено результати:
Також (за Kishore у коментарі нижче):
І (за коментарем акауппі):
За коментарем від cmcromance:
І (за коментарями tencent):
Інші пристрої
Я не перевіряв ці два методи за допомогою спеціального андроїда: LargeHeap = "true" маніфест, доступний з Honeycomb, але завдяки cmcromance та tencent у нас є кілька вибіркових значень bigHeap, як повідомлялося вище.
Моє сподівання (яке, мабуть, підтримується великими числами BigHeap вище), було б, що цей параметр матиме ефект, подібний до встановлення купи вручну через вкорінену ОС - тобто, це підвищить значення maxMemory()
поки залишаючиgetMemoryClass()
спокої. Існує ще один метод getLargeMemoryClass (), який вказує, скільки пам’яті можна використовувати для програми, використовуючи налаштування bigHeap. У документації для getLargeMemoryClass () зазначено, "більшості програм не потрібна ця кількість пам'яті, а замість цього повинна залишатися межа getMemoryClass ()."
Якщо я правильно здогадався, то використання цього параметра матиме ті самі переваги (і небезпеки), як і використання місця, доступного користувачеві, який збільшив купу через вкорінену ОС (тобто, якщо ваш додаток використовує додаткову пам'ять, це, ймовірно, не буде грати так добре, як і інші програми, які користувач одночасно працює).
Зауважте, що клас пам'яті, мабуть, не повинен бути кратним 8 Мб.
З вищевикладеного ми бачимо, що getMemoryClass()
результат є незмінним для заданої конфігурації пристрою / ОС, тоді як значення maxMemory () змінюється, коли купа встановлюється інакше.
Мій власний практичний досвід полягає в тому, що на G1 (у якого клас пам’яті 16), якщо я вручну вибираю 24 Мб як розмір купи, я можу працювати без помилок, навіть коли моє використання пам’яті дозволено зростати до 20 МБ (імовірно, це може ідіть до 24 Мб, хоча я цього не пробував). Але інші подібні програми з великим результатом можуть спалахнути з пам’яті в результаті пиггітності мого власного додатка. І, навпаки, моя додаток може змити з пам’яті, якщо ці інші програми, що потребують високого обслуговування, будуть виведені на перший план користувачем.
Отже, ви не можете перевищити об'єм пам'яті, визначений maxMemory()
. І вам слід спробувати залишитися в межах, визначених getMemoryClass()
. Одним із способів зробити це, якщо все інше не вдасться, може бути обмеження функціональності таких пристроїв таким чином, щоб зберегти пам'ять.
Нарешті, якщо ви плануєте перевищувати кількість мегабайт, зазначених у getMemoryClass()
, моєю порадою буде довго і наполегливо працювати над збереженням та відновленням стану вашої програми, щоб практична робота користувача була безперебійною, якщо onStop()
/onResume()
цикл.
У моєму випадку з міркувань продуктивності я обмежую свій додаток пристроями, на яких працює версія 2.2 і вище, а це означає, що майже всі пристрої, на яких працює моє додаток, матимуть клас пам'яті 24 або вище. Тож я можу створити до 20 Мб купи і відчувати себе досить впевнено, що моє додаток буде чудово грати з іншими програмами, якими користувач може працювати одночасно.
Але завжди знайдеться кілька вкорінених користувачів, які завантажили версію Android 2.2 або вище на більш старий пристрій (наприклад, G1). Якщо ви стикаєтеся з такою конфігурацією, в ідеалі вам слід зменшити використання пам’яті, навіть якщо maxMemory()
вам скажуть, що ви можете набагато перевищувати 16MB, що getMemoryClass()
говорить про те, що вам слід орієнтуватися. І якщо ви не можете надійно переконатися, що ваш додаток працюватиме в межах цього бюджету, то принаймні переконайтеся, що onStop()
/ onResume()
працює безперебійно.
getMemoryClass()
, як зазначено Діаною Хакборн (хакбод) вище, доступний лише для рівня API 5 (Android 2.0), і тому, як вона радить, можна припустити, що фізичне обладнання будь-якого пристрою, на якому працює більш рання версія ОС, розроблено щоб оптимально підтримувати додатки, що займають купу місця не більше 16 Мб.
На відміну від цього maxMemory()
, в відповідно до документації, доступний весь шлях назад до рівня API 1. maxMemory()
на попередній версії 2.0, ймовірно , повертає значення 16MB, але я робити бачимо , що в моїх (багато пізніше) CyanogenMod версій користувачів Ви можете вибрати значення купи в межах 12 МБ, що, мабуть, призведе до нижньої межі нагромадження, і тому я б запропонував продовжувати тестувати maxMemory()
значення навіть для версій ОС до 2.0. Можливо, вам доведеться навіть відмовитись від запуску в тому випадку, якщо це значення встановлено навіть нижче 16 МБ, якщо вам потрібно мати більше, ніж maxMemory()
вказується.
Debug.getNativeHeapSize()
зроблять трюк, я повинен подумати. Хоча він там ще з 1,0 року.
У Debug
класі є безліч чудових методів відстеження розподілу та інших проблем, пов'язаних з ефективністю. Також, якщо вам потрібно виявити ситуацію з низькою пам’яттю, ознайомтесь Activity.onLowMemory()
.
Ось як це зробити:
Отримання максимального розміру купи, яку може використовувати програма:
Runtime runtime = Runtime.getRuntime();
long maxMemory=runtime.maxMemory();
Отримання кількості купи, яку зараз використовує ваш додаток:
long usedMemory=runtime.totalMemory() - runtime.freeMemory();
Отримання кількості купи вашої програми тепер може використовувати (наявна пам'ять):
long availableMemory=maxMemory-usedMemory;
І, щоб добре відформатувати кожен з них, ви можете використовувати:
String formattedMemorySize=Formatter.formatShortFileSize(context,memorySize);
Це повертає максимальний розмір купи в байтах:
Runtime.getRuntime().maxMemory()
Я використовував ActivityManager.getMemoryClass (), але на CyanogenMod 7 (я не перевіряв його в іншому місці) він повертає неправильне значення, якщо користувач встановлює розмір купи вручну.
getMemoryClass
здається, doc for означає, що число може не збігатися з наявним розміром купи для вашого vm, а оскільки doc для getNativeHeapSize
... негласне, я дійсно думаю, що Runtime.getRuntime().maxMemory()
це найкраща відповідь.
Деякі операції проходять швидше, ніж диспетчер простору купі 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
- ваш власний параметр, поріг, при якому ви починаєте чекати, коли сміттєзбірник виконує свою роботу.
Asus Nexus 7 (2013) 32Gig: getMemoryClass () = 192 maxMemory () = 201326592
Я зробив помилку прототипування своєї гри на Nexus 7, а потім виявив, що вона майже відразу втратила пам'ять на загальному планшетному планшеті 4,04 моєї дружини (memoryclass 48, maxmemory 50331648)
Мені потрібно буде реструктурувати мій проект, щоб завантажити менше ресурсів, коли я визначу, що клас пам'яті є низьким.
Чи існує спосіб у Java побачити поточний розмір купи? (Я чітко бачу це в logCat під час налагодження, але я хотів би спосіб його бачити в коді для адаптації, як, наприклад, якщо currentheap> (maxmemory / 2) вивантажує високоякісні растрові карти завантажувати низьку якість
Ви маєте на увазі програмно чи просто під час розробки та налагодження? Якщо останнє, ви можете бачити цю інформацію з точки зору DDMS у програмі Eclipse. Коли ваш емулятор (можливо, навіть фізичний телефон, який підключений), він перелічить активні процеси у вікні зліва. Ви можете вибрати його, і є можливість відстеження розподілу купи.
Runtime rt = Runtime.getRuntime();
rt.maxMemory()
значення - b
ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
am.getMemoryClass()
значення - MB
rt
? Де це оголошено?