У меня есть приложение, которое читает файл CSV с кучами строк данных. Я даю пользователю сводную информацию о количестве строк на основе типов данных, но хочу убедиться, что я не читаю слишком много строк данных и вызываю OutOfMemoryError
s. Каждая строка переводится в объект. Есть ли простой способ узнать размер этого объекта программно? Есть ли ссылка, которая определяет, насколько большие примитивные типы и ссылки на объекты для VM
?
Прямо сейчас у меня есть код, который читает до 32 000 строк , но я также хотел бы, чтобы код читал как можно больше строк, пока я не использовал 32 МБ памяти. Может быть, это другой вопрос, но я все еще хотел бы знать.
Ответы:
Вы можете использовать пакет java.lang.instrument
Скомпилируйте и поместите этот класс в JAR:
Добавьте следующее к вашему
MANIFEST.MF
:Используйте getObjectSize:
Вызвать с:
источник
byte[0]
,byte[1]
,byte[5]
,int[0]
,int[1]
,int[2]
используя подход , вы описали? Было бы хорошо, если бы результаты включали накладные расходы на длину массива и выравнивание памяти.Вам следует использовать jol , инструмент, разработанный как часть проекта OpenJDK.
Чтобы получить размеры примитивов, ссылок и элементов массива, используйте
VMSupport.vmDetails()
. В Oracle JDK 1.8.0_40, работающем в 64-битной Windows (используется для всех следующих примеров), этот метод возвращаетВы можете получить небольшой размер экземпляра объекта, используя
ClassLayout.parseClass(Foo.class).toPrintable()
(опционально передавая экземплярtoPrintable
). Это только пространство, занимаемое одним экземпляром этого класса; он не включает никаких других объектов, на которые ссылается этот класс. Он включает в себя служебные данные VM для заголовка объекта, выравнивания полей и заполнения. Дляjava.util.regex.Pattern
:Вы можете получить сводный вид глубинного размера экземпляра объекта, используя
GraphLayout.parseInstance(obj).toFootprint()
. Конечно, некоторые объекты в элементе footprint могут быть общими (также на них ссылаются другие объекты), так что это избыточное приближение пространства, которое можно восстановить, когда этот объект собирается мусором. Для результатаPattern.compile("^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$")
(взятого из этого ответа ) jol сообщает об общем размере 1840 байт, из которых только 72 являются самим экземпляром Pattern.Если вы вместо этого используете
GraphLayout.parseInstance(obj).toPrintable()
, jol сообщит вам адрес, размер, тип, значение и путь разыменования полей к каждому объекту, на который ссылаются, хотя обычно это слишком много деталей, чтобы быть полезным. Для текущего примера шаблона вы можете получить следующее. (Адреса, вероятно, будут меняться между пробегами.)Записи ((что-то еще)) описывают другие объекты в куче, которые не являются частью этого графа объектов .
Лучшая документация jol - это образцы jol в репозитории jol. В примерах демонстрируются общие операции jol и показано, как можно использовать jol для анализа виртуальных машин и внутренних компонентов сборщика мусора.
источник
vmDetails
сейчасVM.current().details()
.GraphLayout.parseInstance(instance).toFootprint()
я нашел более полезным для понимания размеров объектовЯ случайно нашел java-класс "jdk.nashorn.internal.ir.debug.ObjectSizeCalculator", уже в jdk, который прост в использовании и кажется весьма полезным для определения размера объекта.
Результаты:
источник
ObjectSizeCalculator
поддерживается только на HotSpot VMНесколько лет назад в Javaworld была статья по определению размера составных и потенциально вложенных объектов Java , в основном они описывают создание реализации sizeof () в Java. Этот подход в основном основан на другой работе, в которой люди экспериментально определили размер примитивов и типичных объектов Java, а затем применили эти знания к методу, который рекурсивно просматривает граф объектов для подсчета общего размера.
Он всегда будет несколько менее точным, чем нативная реализация C, просто из-за того, что происходит за кулисами класса, но это должен быть хороший показатель.
В качестве альтернативы проект SourceForge, называемый соответствующим образом sizeof , предлагает библиотеку Java5 с реализацией sizeof ().
PS Не используйте подход сериализации, нет никакой корреляции между размером сериализованного объекта и объемом памяти, который он потребляет при работе.
источник
Во-первых, «размер объекта» не является четко определенной концепцией в Java. Вы можете иметь в виду сам объект, только с его членами, объектом и всеми объектами, на которые он ссылается (граф ссылок). Вы можете иметь в виду размер в памяти или размер на диске. И JVM разрешено оптимизировать такие вещи, как строки.
Поэтому единственный правильный способ - спросить JVM с хорошим профилировщиком (я использую YourKit ), что, вероятно, не то, что вы хотите.
Однако из приведенного выше описания звучит так, что каждая строка будет автономной и не будет иметь большого дерева зависимостей, поэтому метод сериализации, вероятно, будет хорошим приближением для большинства JVM. Самый простой способ сделать это заключается в следующем:
Помните, что если у вас есть объекты с общими ссылками, это не даст правильного результата, и размер сериализации не всегда будет соответствовать размеру в памяти, но это хорошее приближение. Код будет немного более эффективным, если вы инициализируете размер ByteArrayOutputStream разумным значением.
источник
Если вы просто хотите узнать, сколько памяти используется в вашей JVM и сколько свободно, вы можете попробовать что-то вроде этого:
редактирование: я подумал, что это может быть полезно, так как автор вопроса также заявил, что он хотел бы иметь логику, которая обрабатывает «чтение как можно большего количества строк, пока я не использую 32 МБ памяти».
источник
Еще когда я работал в Twitter, я написал утилиту для расчета глубокого размера объекта. Он учитывает различные модели памяти (32-битные, сжатые операции, 64-битные), заполнение, заполнение подклассов, корректно работает с циклическими структурами данных и массивами. Вы можете просто скомпилировать этот файл .java; у него нет внешних зависимостей:
https://github.com/twitter/commons/blob/master/src/java/com/twitter/common/objectsize/ObjectSizeCalculator.java
источник
Большая часть других ответов предоставляет небольшие размеры - например, размер HashMap без каких-либо ключей или значений, что, скорее всего, не то, что вам нужно.
Проект jamm использует пакет java.lang.instrumentation, описанный выше, но обходит дерево и поэтому может дать вам глубокое использование памяти.
https://github.com/jbellis/jamm
источник
Вы должны ходить по объектам, используя отражение. Будьте осторожны, как вы делаете:
byte
теоретически 1 байт не означает, что он занимает только один в памяти.HashMap
или несколько использовать объект-равно в качестве компаратора для устранения бесконечных циклов.@jodonnell: мне нравится простота вашего решения, но многие объекты не являются сериализуемыми (поэтому это может вызвать исключение), поля могут быть временными, а объекты могут переопределять стандартные методы.
источник
Вы должны измерить его с помощью инструмента или оценить вручную, и это зависит от используемой вами JVM.
Существует несколько фиксированных накладных расходов на объект. Это зависит от JVM, но обычно я оцениваю 40 байтов. Тогда вы должны посмотреть на членов класса. Ссылки на объекты составляют 4 (8) байта в 32-битной (64-битной) JVM. Примитивные типы:
Массивы следуют тем же правилам; то есть это ссылка на объект, которая занимает 4 (или 8) байта в вашем объекте, а затем его длину, умноженную на размер его элемента.
Попытка сделать это программно с помощью вызовов
Runtime.freeMemory()
просто не даст вам большой точности из-за асинхронных вызовов сборщика мусора и т. Д. Профилирование кучи с помощью -Xrunhprof или других инструментов даст вам наиболее точные результаты.источник
boolean[]
. На самом деле все примитивы не двойного / длинного типа имеют размер 4 байта. Последние 8 (ответ ошибочно ставит их как 4 тоже)java.lang.instrument.Instrumentation
Класс обеспечивает хороший способ получить размер объекта Java, но он требует , чтобы определитьpremain
и запустить программу с Java агентом. Это очень скучно, когда вам не нужен какой-либо агент, а затем вы должны предоставить фиктивный агент Jar для вашего приложения.Таким образом, я получил альтернативное решение, используя
Unsafe
класс изsun.misc
. Таким образом, учитывая выравнивание кучи объектов в соответствии с архитектурой процессора и вычисляя максимальное смещение поля, вы можете измерить размер Java-объекта. В приведенном ниже примере я использую вспомогательный классUtilUnsafe
для получения ссылки наsun.misc.Unsafe
объект.источник
Существует также инструмент Memory Measurer (ранее в Google Code , теперь в GitHub ), который прост и опубликован под коммерческой лицензией Apache 2.0 , как обсуждалось в аналогичном вопросе .
Он также требует аргумента командной строки для интерпретатора Java, если вы хотите измерить потребление памяти в байтах, но в остальном кажется, что он работает просто отлично, по крайней мере в тех сценариях, которые я использовал.
источник
Без необходимости возиться с инструментарием и т. Д., И если вам не нужно знать точный размер объекта в байтах, вы можете использовать следующий подход:
Таким образом, вы читаете использованную память до и после, и, вызывая GC непосредственно перед получением использованной памяти, вы понижаете «шум» почти до 0.
Для более надежного результата вы можете выполнить свою работу n раз, а затем разделить использованную память на n, получив, сколько памяти занимает один запуск. Более того, вы можете запустить все это больше раз и получить среднее значение.
источник
System.gc()
просто уведомить, что вы хотите GC? Не гарантируется, что GC вызывается вообще.Вот утилита, которую я сделал, используя некоторые из связанных примеров для обработки 32-битных, 64-битных и 64-битных сжатых ООП. Это использует
sun.misc.Unsafe
.Используется
Unsafe.addressSize()
для получения размера собственного указателя иUnsafe.arrayIndexScale( Object[].class )
размера ссылки на Java.Он использует смещение поля известного класса для определения базового размера объекта.
источник
Instrumentation
потому что я не запускаю tomcat,ObjectSizeCalculator
потому что не уверен в типе VM (HotSpot) иJOL
бобовых весенних бобах. Я использую это и добавляю второй параметр для игнорирования синглетонов, а именно кодAbstractRefreshableApplicationContext.getBeanFactory().getSingletonMutex()
рефакторинга,internalSizeOf
чтобы игнорировать Class и EnumЯ искал во время выполнения вычисления размера объекта, который удовлетворял бы следующим требованиям:
Нижеследующее основано на основном коде исходной статьи специалистов по Java ( https://www.javaspecialists.eu/archive/Issue078.html ) и нескольких фрагментах небезопасной версии в другом ответе на этот вопрос.
Я надеюсь, что кто-то найдет это полезным.
}
источник
Нет вызова метода, если это то, что вы просите. С небольшим исследованием, я полагаю, вы могли бы написать свой собственный. Конкретный экземпляр имеет фиксированный размер, полученный из числа ссылок и примитивных значений плюс данные учета экземпляра. Вы бы просто шли по графу объектов. Чем менее разнообразны типы строк, тем легче.
Если это слишком медленно или просто больше проблем, чем стоит, всегда найдется старое доброе правило подсчета строк.
источник
Я написал быстрый тест один раз, чтобы оценить на лету:
Общая концепция заключается в распределении объектов и измерении изменений в свободном пространстве кучи. Ключ
getFreeMemory()
, который запрашивает GC, запускается и ожидает стабилизации сообщенного размера свободной кучи . Результат вышеупомянутого:Что мы и ожидаем, учитывая поведение выравнивания и возможные издержки заголовка блока кучи.
Метод КИП, подробно изложенный в принятом ответе, здесь наиболее точен. Метод, который я описал, является точным, но только в контролируемых условиях, когда никакие другие потоки не создают / отбрасывают объекты.
источник
Просто используйте Java Visual VM.
В нем есть все необходимое для профилирования и устранения проблем с памятью.
Он также имеет консоль OQL (Object Query Language), которая позволяет вам делать много полезных вещей, одним из которых является
sizeof(o)
источник
При использовании JetBrains IntelliJ сначала включите «Присоединить агент памяти» в меню «Файл» | Настройки | Сборка, выполнение, развертывание | Debugger.
При отладке щелкните правой кнопкой мыши интересующую переменную и выберите «Рассчитать оставшийся размер»:
источник
Мой ответ основан на коде, предоставленном Ником. Этот код измеряет общее количество байтов, которые заняты сериализованным объектом. Так что это фактически измеряет материал сериализации + объем памяти обычного объекта (просто сериализуйте, например,
int
и вы увидите, что общее количество сериализованных байтов не так4
). Поэтому, если вы хотите получить необработанный номер байта, используемый именно для вашего объекта - вам нужно немного изменить этот код. Вот так:Я протестировал это решение с примитивными типами, String и на некоторых тривиальных классах. Также могут быть не охваченные случаи.
ОБНОВЛЕНИЕ: Пример изменен для поддержки вычисления объема памяти для объектов массива.
источник
Вы можете сгенерировать дамп кучи (например, с помощью jmap), а затем проанализировать вывод, чтобы найти размеры объектов. Это автономное решение, но вы можете исследовать мелкие и глубокие размеры и т. Д.
источник
размер дает вам увеличение использования памяти jvm из-за создания объекта, и это, как правило, размер объекта.
источник
Этот ответ не связан с размером объекта, но когда вы используете массив для размещения объектов; какой объем памяти он выделит для объекта.
Таким образом, массивы, списки или карты всех этих коллекций не будут на самом деле хранить объекты (только во время примитивов, необходим реальный объем памяти объекта), он будет хранить только ссылки на эти объекты.
Сейчас
Used heap memory = sizeOfObj + sizeOfRef (* 4 bytes) in collection
примитивов
ОБЪЕКТЫ
Я имею в виду, что весь объект REFERENCE требует только 4 байта памяти. Это может быть ссылка на строку ИЛИ двойная ссылка на объект, но в зависимости от создания объекта требуемая память будет варьироваться.
Например, если я создаю объект для приведенного ниже класса,
ReferenceMemoryTest
то будет создано 4 + 4 + 4 = 12 байт памяти. Память может отличаться, когда вы пытаетесь инициализировать ссылки.Поэтому при создании массива объектов / ссылок все его содержимое будет занято пустыми ссылками. И мы знаем, что каждая ссылка требует 4 байта.
И, наконец, выделение памяти для приведенного ниже кода составляет 20 байтов.
ReferenceMemoryTest ref1 = new ReferenceMemoryTest (); (4 (ref1) + 12 = 16 байт) ReferenceMemoryTest ref2 = ref1; (4 (ref2) + 16 = 20 байт)
источник
Предположим, я объявил класс с именем
Complex
вроде:Чтобы увидеть, сколько памяти выделено для живых экземпляров этого класса:
источник
Для JSONObject вам может помочь приведенный ниже код.
возвращает размер в байтах
Я проверил это с моим объектом JSONArray, записав его в файл. Это дает размер объекта.
источник
Я сомневаюсь, что вы хотите сделать это программно, если только вы не хотите сделать это один раз и сохранить его для будущего использования. Это дорогая вещь. В Java нет оператора sizeof (), и даже если бы он существовал, он только посчитал бы стоимость ссылок на другие объекты и размер примитивов.
Один из способов сделать это - сериализовать объект в файл и посмотреть на размер файла, например так:
Конечно, это предполагает, что каждый объект является отдельным и не содержит непереходных ссылок на что-либо еще.
Другая стратегия состояла бы в том, чтобы взять каждый объект и изучить его элементы путем отражения и сложить размеры (логическое значение & byte = 1 байт, short & char = 2 байта и т. Д.), Прокладывая путь вниз по иерархии членства. Но это утомительно и дорого и в итоге делает то же самое, что и стратегия сериализации.
источник
java.lang.Integer
дает около 80 байтов, где представление кучи обычно равно 32 (в отличие от представления потока объекта, представление кучи зависит от размеров указателя и выравнивания объекта). Напротив, для сериализованнойnull
ссылки требуется один байт вместо четырех или восьми байтов в памяти кучи.