Intereting Posts
Отключить TabLayout Ориентация Изменено событие или прослушиватель Android Android 4.4 Выбор файла WebView не открывается? SharedPreference, установленное в SyncAdapter, не обновлено в Activity? Круговая контрольная панель MediaPlayer больше не работает с эмулятором с r10? Почему происходит утечка памяти Android из-за статического Drawable, если обратный вызов сброшен? Как обработчик влияет на способ вызова onReceiveResult (ResultReceiver)? Отображение нелинейной структуры данных, такой как дерево в Android View API-интерфейс AppCompatButton backgroundTint <21 Завершение кода в Android Studio Контролируя интенсивность вибрации в телефонах Android? Является ли это возможным? Обработка аннотации Kotlin игнорирует элементы с похожими именами Просмотр фрагмента ViewPager's, потерянный при отображении родительского фрагмента ViewPager, затем отображается Проблема с клавиатурой при фокусировке на нижней части текста редактирования на Android

Далвик еще больше голоден, чем HotSpot с точки зрения размеров объектов?

Мне интересно, сколько памяти занимает объект на Android. Существует множество ресурсов (подобных этому ), связанных с JVM HotSpot, которые говорят, что пустой объект занимает 8 байтов, а пустой массив – 12 байтов и что все объекты выровнены с 8-байтовой границей. Таким образом, объект без дополнительных полей должен принимать 8 байтов, наименьший объект с по крайней мере одним дополнительным полем – 16 байтов, пустой массив – 16 байтов, правильно?

Я не нашел никакой конкретной информации о Dalvik по этому вопросу и решил разобраться в этом, тестируя. Запуск теста имел неожиданные результаты .

Несколько слов о методе расчета. Реализация объекта Object.hashCode () для Android выполняется просто, указав указатель на объект, переданный в int. (Казалось очевидным и общим, но [еще один сюрприз], как выяснилось, это НЕ на HotSpot JVM, например, запустить MemTest с помощью HotSpot и посмотреть). Итак, я использовал простоту hashCode () для Dalvik для вычисления размера объекта на Android, выделив два экземпляра тестируемого класса в строке, а объем выделенного пространства должен быть равен разности их hashCode () (При условии, что Dalvik не имеет смысла выделять их на совершенно случайных адресах). Просто чтобы быть уверенным, что я выделил всегда 4 объекта в строке для каждого тестового класса, который всегда всегда отличался от hashCode (). Итак, я считаю, что нет сомнений в правильности метода.

Вот исходный код теста:

public class MemTest { public static void run() { Object o1 = new Object(); Object o2 = new Object(); Object o3 = new Object(); Object o4 = new Object(); EmptyObject eo1 = new EmptyObject(); EmptyObject eo2 = new EmptyObject(); EmptyObject eo3 = new EmptyObject(); EmptyObject eo4 = new EmptyObject(); ObjectWithBoolean ob1 = new ObjectWithBoolean(); ObjectWithBoolean ob2 = new ObjectWithBoolean(); ObjectWithBoolean ob3 = new ObjectWithBoolean(); ObjectWithBoolean ob4 = new ObjectWithBoolean(); ObjectWithBooleanAndInt obi1 = new ObjectWithBooleanAndInt(); ObjectWithBooleanAndInt obi2 = new ObjectWithBooleanAndInt(); ObjectWithBooleanAndInt obi3 = new ObjectWithBooleanAndInt(); ObjectWithBooleanAndInt obi4 = new ObjectWithBooleanAndInt(); ObjectWithLong ol1 = new ObjectWithLong(); ObjectWithLong ol2 = new ObjectWithLong(); ObjectWithLong ol3 = new ObjectWithLong(); ObjectWithLong ol4 = new ObjectWithLong(); ObjectWith4Ints o4i1 = new ObjectWith4Ints(); ObjectWith4Ints o4i2 = new ObjectWith4Ints(); ObjectWith4Ints o4i3 = new ObjectWith4Ints(); ObjectWith4Ints o4i4 = new ObjectWith4Ints(); ObjectWith4IntsAndByte o4ib1 = new ObjectWith4IntsAndByte(); ObjectWith4IntsAndByte o4ib2 = new ObjectWith4IntsAndByte(); ObjectWith4IntsAndByte o4ib3 = new ObjectWith4IntsAndByte(); ObjectWith4IntsAndByte o4ib4 = new ObjectWith4IntsAndByte(); ObjectWith5Ints o5i1 = new ObjectWith5Ints(); ObjectWith5Ints o5i2 = new ObjectWith5Ints(); ObjectWith5Ints o5i3 = new ObjectWith5Ints(); ObjectWith5Ints o5i4 = new ObjectWith5Ints(); ObjectWithArrayRef oar1 = new ObjectWithArrayRef(); ObjectWithArrayRef oar2 = new ObjectWithArrayRef(); ObjectWithArrayRef oar3 = new ObjectWithArrayRef(); ObjectWithArrayRef oar4 = new ObjectWithArrayRef(); byte[] a0b1 = new byte[0]; byte[] a0b2 = new byte[0]; byte[] a0b3 = new byte[0]; byte[] a0b4 = new byte[0]; byte[] a1b1 = new byte[1]; byte[] a1b2 = new byte[1]; byte[] a1b3 = new byte[1]; byte[] a1b4 = new byte[1]; byte[] a5b1 = new byte[5]; byte[] a5b2 = new byte[5]; byte[] a5b3 = new byte[5]; byte[] a5b4 = new byte[5]; byte[] a9b1 = new byte[9]; byte[] a9b2 = new byte[9]; byte[] a9b3 = new byte[9]; byte[] a9b4 = new byte[9]; byte[] a12b1 = new byte[12]; byte[] a12b2 = new byte[12]; byte[] a12b3 = new byte[12]; byte[] a12b4 = new byte[12]; byte[] a13b1 = new byte[13]; byte[] a13b2 = new byte[13]; byte[] a13b3 = new byte[13]; byte[] a13b4 = new byte[13]; print("java.lang.Object", o1, o2, o3, o4); print("Empty object", eo1, eo2, eo3, eo4); print("Object with boolean", ob1, ob2, ob3, ob4); print("Object with boolean and int", obi1, obi2, obi3, obi4); print("Object with long", ol1, ol2, ol3, ol4); print("Object with 4 ints", o4i1, o4i2, o4i3, o4i4); print("Object with 4 ints and byte", o4ib1, o4ib2, o4ib3, o4ib4); print("Object with 5 ints", o5i1, o5i2, o5i3, o5i4); print("Object with array ref", new Object[]{oar1, oar2, oar3, oar4}); print("new byte[0]", a0b1, a0b2, a0b3, a0b4); print("new byte[1]", a1b1, a1b2, a1b3, a1b4); print("new byte[5]", a5b1, a5b2, a5b3, a5b4); print("new byte[9]", a9b1, a9b2, a9b3, a9b4); print("new byte[12]", a12b1, a12b2, a12b3, a12b4); print("new byte[13]", a13b1, a13b2, a13b3, a13b4); } static void print(String title, Object... objects) { StringBuilder buf = new StringBuilder(title).append(":"); int prevHash = objects[0].hashCode(); int prevDiff = -1; for (int i = 1; i < objects.length; i++) { int hash = objects[i].hashCode(); int diff = Math.abs(hash - prevHash); if (prevDiff == -1 || prevDiff != diff) { buf.append(' ').append(diff); } prevDiff = diff; prevHash = hash; } System.out.println(buf.toString()); } /******** Test classes ******/ public static class EmptyObject { } public static class ObjectWith4Ints { int i1; int i2; int i3; int i4; } public static class ObjectWith4IntsAndByte { int i1; int i2; int i3; int i4; byte b; } public static class ObjectWith5Ints { int i1; int i2; int i3; int i4; int i5; } public static class ObjectWithArrayRef { byte[] b; } public static class ObjectWithBoolean { boolean b; } public static class ObjectWithBooleanAndInt { boolean b; int i; } public static class ObjectWithLong { long l; } } 

И вот результаты:

 java.lang.Object: 16 Empty object: 16 Object with boolean: 16 Object with boolean and int: 24 Object with long: 24 Object with 4 ints: 32 Object with 4 ints and byte: 32 Object with 5 ints: 32 Object with array ref: 16 new byte[0]: 24 new byte[1]: 24 new byte[5]: 32 new byte[9]: 32 new byte[12]: 32 new byte[13]: 40 

Подведем итоги:

  • 8-битное выравнивание границы такое же, как и на HotSpot, и это единственное, что то же самое.

  • Минимум 16 байт для простого объекта (vs 8 на HotSpot)

  • По-видимому, пустой объект сам занимает 12 байтов (vs 8 на HotSpot), и есть место для 4 дополнительных байтов, пока размер объекта не «перескочит» с 16 байт до следующей границы из 24 байтов.

  • Минимум 24 байта для пустого массива (против 12 на HotSpot)

  • Аналогично, массив сам занимает 20 байт (vs 12 на HotSpot), и есть место для 4 дополнительных байтов данных массива, пока размер объекта не «перескочит» с 24 байтов до следующей границы 32 байтов.

ДОПОЛНЕНИЕ: (в ответ на предложение Луи). Еще один стресс-тест показывает, что даже выделение миллиона экземпляров объектов расстояние между любыми двумя никогда не меньше 16 байт. Это доказательство того, что потенциальные 8-байтовые дыры между объектами, безусловно, являются мертвым пространством для дальнейших распределений, иначе к тому времени, когда около половины памяти было выделено для объектов, dalvik обязательно должен был помещать некоторые из них в «дыры», И стресс-тест вернет 8, а не 16.

 public static void run2() { int count = 1024 * 1024; Object[] arr = new Object[count]; for (int i = 0; i < count; i++) { arr[i] = new Object(); } int[] hashes = new int[count]; for (int i = 0; i < count; i++) { hashes[i] = arr[i].hashCode(); } Arrays.sort(hashes); int minDist = Integer.MAX_VALUE; for (int i = 1; i < count; i++) { int dist = Math.abs(hashes[i] - hashes[i - 1]); if (dist < minDist) { minDist = dist; } } System.out.println("Allocated "+ count + " Objects, minimum distance is "+ minDist); } 

Правильно ли я вижу, что объект Dalvik занимает до 8 байт, а массив еще на 8-12 байтов по сравнению с HotSpot?

Solutions Collecting From Web of "Далвик еще больше голоден, чем HotSpot с точки зрения размеров объектов?"

(Да, это старый вопрос, но результаты были интересными, поэтому я немного задумался).

Метод Object.clone() должен выполнить полную побитовую копию объекта. Для этого ему нужно знать, насколько большой объект. Если вы посмотрите на dvmCloneObject() , вы увидите, что он использует один метод для массивов и другой метод для объектов.

Для массивов он вызывает dvmArrayObjectSize() , который умножает длину массива на ширину элемента (1, 2, 4 или 8), а затем добавляет смещение данных массива с начала объекта. Каждый объект имеет 8-байтовый заголовок; Массивы имеют ширину в 4 байта и включают дополнительные 4 байта заполнения, чтобы обеспечить правильное выравнивание 64-битных значений. Таким образом, для 5-элементного массива short , это будет 16 + 5 * 2.

Для обычных объектов он просто использует поле objectSize в объекте класса. Это задается довольно сложной функцией, называемой computeFieldOffsets() . Эта функция гарантирует, что все ссылки на объекты будут первыми (так что GC может пропускать меньше при сканировании), а затем следует, что все 64-битные поля. Чтобы обеспечить правильное выравнивание 64-битных полей, он может перенести одно из 32-битных примитивных полей, чтобы выровнять все. (Если нет подходящего 32-битного поля, вы просто получите 4 байта заполнения).

Я должен добавить: все поля 32-бит, за исключением long и double , которые являются 64-битными. Ссылки на объекты 32-разрядные.

Поэтому сложно сказать, насколько велика будет объект без массива, но в целом вы берете 8-байтовый заголовок объекта, суммируете ширину дополнительных полей и округляете до следующего кратного 8 байтов – Последнее, потому что все объекты должны быть согласованы на 64 бита.

Так что это теория. Чтобы увидеть это на практике, я добавил это в dvmCloneObject() :

 ALOGD("class=%s size=%d", clazz->descriptor, clazz->objectSize); 

И увидел выход logcat как:

 D dalvikvm: class=Ljava/util/Locale; size=24 D dalvikvm: class=Ljava/util/Date; size=16 

Locale имеет 4 ссылочных поля, Date имеет одно long поле, поэтому эти значения соответствуют ожиданиям.

В идеале, это именно то, сколько пространства потребуется. Однако объект выделяется с помощью mspace_calloc() , который добавляет еще 4 или (иногда) 8 байтов служебных данных. Таким образом, фактическое пространство, необходимое для значений выше, будет 32 и 24, что соответствует вашим экспериментальным результатам.

У меня нет ответов для вас, но я могу предложить пару мест, которые вы можете посмотреть в источнике, для получения дополнительной информации.

Вы можете взглянуть на структуры DataObject и ArrayObject в dalvik / vm / oo / Object.h. Исходя из этого, кажется, что пустой объект должен принимать только 8 байтов, а пустой массив должен принимать 12 байтов. Это, похоже, не соответствует вашим результатам, хотя я не уверен, почему.

Вы можете также взглянуть на использование поля objectSize в структуре ClassObject для более глубокого понимания. Быстрый поиск для использования этого поля показывает, что метод dvmAllocObject в dalvik / vm / alloc / Alloc.cpp, по-видимому, отвечает за выделение памяти для новых объектов.