Мне нужно узнать, сколько байт в оперативной памяти занимает определенная коллекция в моем приложении (ArrayList
). Нужно это для того, чтобы представлять, сколько объектов я могу в нее добавить, не получив OutOfMemory
.
Как посчитать вручную я представляю, но хотел бы узнать, есть ли способ посчитать программно.
Конкретно свою проблему я решил, но вопрос остается открытым, ибо мое решение не связано с определением объема памяти, занимаемого объекта.
Итак, я просто решил на практике проверить, сколько же объектов без проблем удержится в памяти на разных моделях телефонов.
java.lang.instrument
в Android (Dalvic/ART) не существует.
Runtime.getRuntime().totalMemory() -
Runtime.getRuntime().freeMemory()
до создания достаточно большой тестовой коллекции и после. Повторить несколько раз, чтобы исключить случайности. Более сложный — это использовать java.lang.instrumentation package и т.п. для определения размера среднего объекта в коллекции и по нему прикидывать размер коллекции.
Сам по себе ArrayList
занимает всего 4(8) байт на элемент (в
зависимости от размера указателя, причем даже в 64 битной системе
указатели могут быть 4 байтные), то есть основные затраты памяти
будут именно на объекты, содержащиеся в ArrayList
. Но определение размера объекта в Java вещь сложная, так как не понятно что считать. Если считать только размер самих полей-ссылок — размер объектов будет совсем небольшим, если считать в том числе и все объекты, на которые данные объекты ссылаются, то много объектов могут ссылаться на один общий объект и он посчитается несколько раз (более того все объекты в системе могут ссылаться друг на друга и размер любого объекта можно посчитать равным всем объектам системы). Так что первый способ предпочтительнее.
В процессе исследования своего вопроса набросал класс, который анализирует объект, и считает объем памяти, который тот занимает. Считает не точно. Честно сказать, не знаю даже, с какой погрешностью:
public class MemoryUtils {
private static Map<Class, Integer> primitiveSizes = new HashMap<>();
static {
primitiveSizes.put(byte.class, 1);
primitiveSizes.put(short.class, 2);
primitiveSizes.put(int.class, 4);
primitiveSizes.put(long.class, 8);
primitiveSizes.put(float.class, 4);
primitiveSizes.put(double.class, 8);
primitiveSizes.put(char.class, 2);
primitiveSizes.put(boolean.class, 1);
}
private static Set<Class> boxed = new HashSet<>();
static {
boxed.add(Byte.class);
boxed.add(Short.class);
boxed.add(Integer.class);
boxed.add(Long.class);
boxed.add(Float.class);
boxed.add(Double.class);
boxed.add(Character.class);
boxed.add(Boolean.class);
}
static final int REFERENCE_SIZE = 4;
static long calls = 0;
public static long sizeOf(Object object, List<Object> calculated, Class clazz) {
try {
calls++;
if (calls % 1000 == 0) {
Log.d("MemoryUtils", "call # " + calls);
}
if (calculated == null) {
calculated = new ArrayList<>();
}
if (object == null) {
return 0;
}
long result = 0;
if (clazz == null) {
clazz = object.getClass();
}
if(clazz == String.class) {
return ((String)object).length() * 2 + 38;
}
if (object.getClass() == Object.class) {
return REFERENCE_SIZE * 2;
}
if (clazz.isPrimitive()) {
int size = primitiveSizes.get(clazz);
result += size;
} else if (clazz.isArray()) {
int length = Array.getLength(object);
for (int i = 0; i < length; i++) {
Object element = Array.get(object, i);
if (!calculated.contains(element) && element != null) {
Class typeToPass = element.getClass();
if (boxed.contains(typeToPass)) {
typeToPass = object.getClass().getComponentType();
}
result += sizeOf(element, calculated, typeToPass);
}
}
} else {
result += REFERENCE_SIZE;
calculated.add(object);
for (Field f : clazz.getDeclaredFields()) {
if (java.lang.reflect.Modifier.isStatic(f.getModifiers())) {
continue;
}
f.setAccessible(true);
try {
Object value = f.get(object);
if (!calculated.contains(value) && value != null) {
Class typeToPass = value.getClass();
if (boxed.contains(typeToPass)) {
typeToPass = f.getType();
}
result += sizeOf(value, calculated, typeToPass);
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
Log.d("MemoryUtils", "class = " + clazz.getName() + " value = " + object + " size = " + result + " isPrimitive = " + clazz.isPrimitive());
return result;
} catch (Exception e) {
e.printStackTrace();
}
return 0;
}
}
Использовать так:
MemoryUtils.sizeOf(someObject, null, null);
java -jar jol-cli-0.5-full.jar internals 'java.util.ArrayList'
Objects are 8 bytes aligned.
Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
java.util.ArrayList object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00
4 4 (object header) 00 00 00 00
8 4 (object header) 1e 2f 00 f8
12 4 int AbstractList.modCount 0
16 4 int ArrayList.size 0
20 4 Object[] ArrayList.elementData []
Instance size: 24 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
Update:
Ссылка на Exploring Java's Hidden Costs
Ссылка на jol-cli-0.5-full.jar
Можно попробовать так. На вход засовываете всё, что угодно, на выходе получаем длину byteArray-я :
public static int getMemoryLength(Object object) throws java.io.IOException
{
ByteArrayOutputStream byteObject = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteObject);
objectOutputStream.writeObject(object);
objectOutputStream.flush();
objectOutputStream.close();
byteObject.close();
return byteObject.toByteArray().length;
}
Невозможно узнать сколько памяти будет занимать объект, если изначально не проектировать его под определенный объем.
В большинстве своем хранение информации сводится к хранению символов и чисел.
Можно рассчитать, сколько символов или чисел поместиться в памяти, но не объектов (примитивы можно).
Пример:
Сколько места в памяти займет объект String
?
Ровно столько, сколько в него будет положено символов + служебная информация. Мы можем рассчитать сколько объектов поместиться на 100Mb. Пусть объектами будут отдельные слова и условно у нас помещается 1000 строк по 1 слову.
В runtime
мы начинаем получать не 1 слово, а 3 и со всеми расчетами ловим OutOfMemory
.
Альтернативный путь, ограничение размера объекта изначально как это сделано с примитивами и их обертками. Как бы мы ни старались, положить в int
значение больше 2*32
не получиться. Если свои объекты ограничить так же, то вы сможете рассчитать сколько объектов сможете хранить в памяти.
В принципе если бы вы знали сколько объектов можете хранить в памяти, List
вам был бы не нужен и можно было использовать фиксированный массив
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Перевод документов на английский язык: Важность и ключевые аспекты
Добрый день! Написал тут программу, которая должна считать кол-во дней до какой-то датыСтолкнулся с тем, что не совсем корректно работают...
Задача: Сделать 3 потока, которые будут выполнятся друг за другом, то есть 2-ой поток идет за 1-ым, а 3-ий за вторым, 1-ый за 3-имЗадача должна быть...
андроид студио в интеллии, выскакивает такая ошибка:
Я создал jar файл для java агента для того чтобы использовать его в своем приложении , java агент подсчитывает размер объекта с помощью класса...