Как определить размер объекта в Java?
У меня есть приложение, которое читает CSV файл с большим количеством строк данных. Я предоставляю пользователю сводку о количестве строк на основе типов данных, но хочу убедиться, что не читаю слишком много строк и не вызываю ошибку OutOfMemoryError
. Каждая строка соответствует объекту. Есть ли способ программно определить размер этого объекта? Существует ли ссылка, которая определяет, какого размера примитивные типы и ссылки на объекты для VM
?
На данный момент у меня есть код, который читает не более 32 000 строк, но я также хотел бы иметь код, который читает столько строк, сколько возможно, пока я не использую 32 МБ памяти.
5 ответ(ов)
Вы случайно наткнулись на класс Java, называемый jdk.nashorn.internal.ir.debug.ObjectSizeCalculator
, который уже присутствует в JDK и довольно прост в использовании. Этот класс может быть полезен для определения размера объектов в Java.
Вот пример его использования:
System.out.println(ObjectSizeCalculator.getObjectSize(new gnu.trove.map.hash.TObjectIntHashMap<String>(12000, 0.6f, -1)));
System.out.println(ObjectSizeCalculator.getObjectSize(new HashMap<String, Integer>(100000)));
System.out.println(ObjectSizeCalculator.getObjectSize(3));
System.out.println(ObjectSizeCalculator.getObjectSize(new int[]{1, 2, 3, 4, 5, 6, 7 }));
System.out.println(ObjectSizeCalculator.getObjectSize(new int[100]));
Результаты выполнения этого кода:
164192
48
16
48
416
Таким образом, вы можете видеть, сколько памяти занимает каждый из этих объектов. Например, TObjectIntHashMap
занимает 164192 байта, тогда как примитивный тип int
занимает всего 16 байт. Это может быть полезно для оптимизации использования памяти в ваших приложениях. Однако следует учитывать, что класс ObjectSizeCalculator
находится в внутреннем пакете JDK и его использование не рекомендуется для производственного кода, так как он может измениться или быть удален в будущих версиях JDK.
Если вам нужно узнать, сколько памяти используется в вашей JVM и сколько её свободно, вы можете попробовать следующее:
// Получаем текущий размер кучи в байтах
long heapSize = Runtime.getRuntime().totalMemory();
// Получаем максимальный размер кучи в байтах. Куча не может превышать этот размер.
// Любая попытка выделить больше памяти приведет к OutOfMemoryException.
long heapMaxSize = Runtime.getRuntime().maxMemory();
// Получаем количество свободной памяти в куче в байтах. Этот размер будет увеличиваться
// после сборки мусора и уменьшаться по мере создания новых объектов.
long heapFreeSize = Runtime.getRuntime().freeMemory();
Дополнение: Я подумал, что это может быть полезно, так как автор вопроса также упомянул, что хотел бы реализовать логику, которая будет "читать столько строк, сколько возможно, пока я не использую 32 МБ памяти".
Когда я работал в Twitter, я разработал утилиту для вычисления размера глубоких объектов. Она учитывает различные модели памяти (32-битные, сжатые oops, 64-битные), добавляет выравнивание, учитывает выравнивание подклассов и корректно работает с круговыми структурами данных и массивами. Вам просто нужно скомпилировать этот один .java файл; у него нет внешних зависимостей:
Большинство других ответов предоставляют поверхностные размеры - например, размер HashMap без учета ключей или значений, что, вероятно, вам не нужно.
Проект jamm использует пакет java.lang.instrumentation, но проходит по дереву объектов и может предоставить глубокое использование памяти.
new MemoryMeter().measureDeep(myHashMap);
Подробности можно найти на GitHub.
Чтобы использовать MemoryMeter, запустите JVM с параметром "-javaagent:/jamm.jar".
Вы должны пройтись по объектам с помощью рефлексии. Будьте осторожны в этом процессе:
- Просто выделение объекта создает некоторые накладные расходы в JVM. Размер этих расходов может варьироваться в зависимости от используемой JVM, поэтому имеет смысл сделать это значение параметром. По крайней мере, сделайте его константой (например, 8 байт) и применяйте к любому выделенному объекту.
- Несмотря на то, что
byte
теоретически занимает 1 байт, в памяти он может занимать больше места. - В объектах могут быть циклические ссылки, поэтому вам нужно будет использовать
HashMap
или что-то подобное с использованием метода equals для сравнения объектов, чтобы избежать бесконечных циклов.
@jodonnell: Мне нравится простота вашего решения, но многие объекты не реализуют интерфейс Serializable (что приведет к выбросу исключения), поля могут быть временными, а объекты могут переопределять стандартные методы.
Как создать утечку памяти в Java?
Инициализация ArrayList в одну строчку
Каков эквивалент статических методов Java в Kotlin?
Что такое «сырые типы» и почему их не следует использовать?
Как явно освободить память в Python?