Виявлення розміру купи додатків в Android


145

Як ви програматично визначаєте розмір купи програми, доступний для програми Android?

Я чув, що є функція, яка робить це в пізніших версіях SDK. У будь-якому випадку я шукаю рішення, яке працює від 1,5 і вище.


Відповіді:


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" / "Продуктивність" / "розмір купівлі VM".

ПРИМІТКА. Будьте уважні, що НАСТРОЮВАННЯ ЦІЙ ЦІННОСТІ МОЖЛИВИ ЗМЕСТИТИ ВАШУ СИСТЕМУ, ОСОБЛИВО, якщо ви виберете менше значення, ніж це нормально для вашого пристрою.

Ось мої результати тестування, які показують значення, повернені maxMemory()і getMemoryClass()для чотирьох різних пристроїв, на яких працює CyanogenMod, використовуючи два різні (вручну встановлені) значення купи для кожного:

  • G1:
    • З розміром VM Heap розмір встановлений на 16 Мб:
      • maxMemory: 16777216
      • getMemoryClass: 16
    • З розміром VM Heap розмір встановлений на 24 Мб:
      • maxMemory: 25165824
      • getMemoryClass: 16
  • Moto Droid:
    • З розміром VM Heap розмір встановлений на 24 Мб:
      • maxMemory: 25165824
      • getMemoryClass: 24
    • З розміром VM Heap розмір встановлений на 16 Мб:
      • maxMemory: 16777216
      • getMemoryClass: 24
  • Nexus One:
    • З розміром VM Heap встановлено 32MB:
      • maxMemory: 33554432
      • getMemoryClass: 32
    • З розміром VM Heap встановлено 24 Мб:
      • maxMemory: 25165824
      • getMemoryClass: 32
  • Viewsonic GTab:
    • З розміром VM Heap розмір 32
      • maxMemory: 33554432
      • getMemoryClass: 32
    • З розміром VM Heap розмір встановлений на 64:
      • maxMemory: 67108864
      • getMemoryClass: 32

На додаток до вищезазначеного, я протестував на планшеті Paladin Novo7, де працює сендвіч з морозивом. По суті це була біржова версія ICS, за винятком того, що я вкоренив планшет через простий процес, який не замінює всю ОС, і, зокрема, не забезпечує інтерфейс, який би дозволяв регулювати розмір купи вручну.

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

  • Novo7
    • maxMemory: 62914560
    • getMemoryClass: 60

Також (за Kishore у коментарі нижче):

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

І (за коментарем акауппі):

  • Samsung Galaxy Core Plus
    • maxMemory: (Не вказано в коментарі)
    • getMemoryClass: 48
    • великийMemoryClass: 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 (4.4.2)
    • maxMemory: 536870912
    • getMemoryClass: 192

Інші пристрої

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

Я не перевіряв ці два методи за допомогою спеціального андроїда: 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()вказується.


2
@Carl Привіт Карл, дякую за прекрасний пост. І я тестував варіант, android: largeHeap = "справжній" на Galaxy S3 з JellyBean. Ось результат. [maxMemory: 256.0MB, memoryClass: 64MB] (maxMemory становив 64MB без опції великого кута)
cmcromance

1
@Carl Haha Це велика честь! :)
cmcromance

1
Для HTC One X: maxMemory: 67108864 memoryClass: 64
Kishore

1
LG Nexus 5 (4.4.3) (звичайний): maxMemory: 201326592, memoryClass: 192 | LG Nexus 5 (4.4.3) (велика купа): maxMemory: 536870912, memoryClass: 192
ian.shaun.thomas

1
Дуже красиво задокументовано. Надайте це на андроїд. Я не можу знайти такий відповідний документ для Android
Jimit Patel

20

Офіційний API :

Це було введено в 2.0, де з'явилися більші пристрої пам'яті. Можна припустити, що пристрої, на яких працює попередні версії ОС, використовують оригінальний клас пам'яті (16).


13

Debug.getNativeHeapSize()зроблять трюк, я повинен подумати. Хоча він там ще з 1,0 року.

У Debugкласі є безліч чудових методів відстеження розподілу та інших проблем, пов'язаних з ефективністю. Також, якщо вам потрібно виявити ситуацію з низькою пам’яттю, ознайомтесь Activity.onLowMemory().


Дякую Нілу. Чи працює це, коли програма запущена з вимкненою налагодженням?
hpique

2
Я досить новачок у цьому, але не думаю, що рідна купа - це те саме, що і додаток (Dalvik), до якого відноситься питання. Народний купа забезпечує резервну копію растрових даних, яка розподіляється за допомогою власного коду, тоді як додаток містить дані програми Java. Мені було цікаво дізнатись, що рідна купа зараховується до межі купи додатків, і після 3.0 виділення фактично відбуваються на купі додатка. Діана Хакборн (хакбод) публікує цю тему тут: 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); 

5

Це повертає максимальний розмір купи в байтах:

Runtime.getRuntime().maxMemory()

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


Оскільки, getMemoryClassздається, doc for означає, що число може не збігатися з наявним розміром купи для вашого vm, а оскільки doc для getNativeHeapSize... негласне, я дійсно думаю, що Runtime.getRuntime().maxMemory()це найкраща відповідь.
BoD

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? деякі пояснення?
Noor

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) вивантажує високоякісні растрові карти завантажувати низьку якість


За вказаним 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()

значення - MB


Якого типу rt? Де це оголошено?
FindOut_Quran
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.