У меня есть приложение, которое читает файл CSV с кучей строк данных. Я даю пользователю сводку количества строк на основе типов данных, но я хочу убедиться, что я не читаю слишком много строк данных и не вызываю OutOfMemoryError. Каждая строка превращается в объект. Есть ли простой способ узнать размер этого объекта программно? Есть ли ссылка, определяющая, насколько велики примитивные типы и ссылки на объекты для VM?
Прямо сейчас у меня есть код, который гласит, что читать до 32000 строк, но я также хотел бы иметь код, который говорит, что читать как можно больше строк, пока я не использую 32 МБ памяти. Может быть, это другой вопрос, но мне все же хотелось бы знать.




Я сомневаюсь, что вы хотите делать это программно, если только вы не хотите сделать это один раз и сохранить для будущего использования. Это дорогое удовольствие. В Java нет оператора sizeof (), и даже если бы он был, он подсчитывал бы только стоимость ссылок на другие объекты и размер примитивов.
Один из способов сделать это - сериализовать вещь в файл и посмотреть размер файла, например:
Serializable myObject;
ObjectOutputStream oos = new ObjectOutputStream (new FileOutputStream ("obj.ser"));
oos.write (myObject);
oos.close ();
Конечно, это предполагает, что каждый объект уникален и не содержит непреходящих ссылок на что-либо еще.
Другой стратегией было бы взять каждый объект и исследовать его элементы путем отражения и сложить размеры (логический & byte = 1 байт, короткий & char = 2 байта и т. д.), Продвигаясь вниз по иерархии членства. Но это утомительно и дорого и в конечном итоге делает то же самое, что и стратегия сериализации.
Я бы сериализовал его в byte [], используя ByteArrayOutputStream. Это будет намного быстрее, чем записать его в файл.
@KorayTugay Определение байтового размера объекта - уже дорогостоящая операция. Запись каждого объекта на диск для определения размера просто заставит его ползать ...
Формат сериализованного объекта полностью отличается от формата объекта в динамической памяти. В частности, в поток записывается дескриптор класса объекта (и всех его сериализуемых суперклассов). Таким образом, запись простого экземпляра java.lang.Integer дает около 80 байтов, где представление кучи обычно равно 32 (в отличие от представления потока объектов, представление кучи зависит от размеров указателя и выравнивания объекта). Напротив, для сериализованной ссылки null требуется один байт вместо четырех или восьми байтов в динамической памяти.
Нет вызова метода, если это то, о чем вы просите. Я полагаю, что после небольшого исследования вы могли бы написать свой собственный. Конкретный экземпляр имеет фиксированный размер, полученный на основе количества ссылок и примитивных значений, а также данных бухгалтерского учета экземпляра. Вы могли бы просто пройтись по графу объекта. Чем менее разнообразны типы строк, тем проще.
Если это слишком медленно или просто больше проблем, чем оно того стоит, всегда есть доброе старомодное правило подсчета строк.
Если вы просто хотите узнать, сколько памяти используется в вашей JVM и сколько свободно, вы можете попробовать что-то вроде этого:
// Get current size of heap in bytes
long heapSize = Runtime.getRuntime().totalMemory();
// Get maximum size of heap in bytes. The heap cannot grow beyond this size.
// Any attempt will result in an OutOfMemoryException.
long heapMaxSize = Runtime.getRuntime().maxMemory();
// Get amount of free memory within the heap in bytes. This size will increase
// after garbage collection and decrease as new objects are created.
long heapFreeSize = Runtime.getRuntime().freeMemory();
edit: Я подумал, что это может быть полезно, поскольку автор вопроса также заявил, что хотел бы иметь логику, которая обрабатывает «чтение как можно большего количества строк, пока я не использую 32 МБ памяти».
Это не лучшее решение, так как вы никогда не знаете, когда произойдет сборка мусора или сколько дополнительной памяти будет выделено для кучи за один раз.
Это правда, и я бы не собирался этим заниматься, чтобы затронуть главный вопрос этого поста, но это может помочь ему программно узнать, когда он приближается к достижению максимального размера кучи.
Другая проблема этого решения - когда вы находитесь в многопоточной среде (например, на веб-сервере). Возможно, что другие потоки выполнялись и потребляли память. Используя это приближение, вы рассчитываете используемую память на всей виртуальной машине.
Еще один недостаток - freeMemory возвращает приблизительное значение. Попробуйте создать объект javax.crypto.Cipher. Разница между двумя вызовами freeMemory (для оценки размера Cipher) непостоянна!
Здравствуйте, почему heapSize + heapFreeSize <heapMaxSize?
Я считаю, что вы можете принудительно выполнить сборку мусора, поэтому вы может кое-что сделаете в этом подходе.
Вы должны обходить объекты, используя отражение. Будьте осторожны, как вы:
byte теоретически составляет 1 байт, не означает, что он занимает всего один байт в памяти.HashMap или что-то вроде использование равных по объекту в качестве компаратора, чтобы исключить бесконечные циклы.@jodonnell: мне нравится простота вашего решения, но многие объекты не поддерживают сериализацию (так что это вызовет исключение), поля могут быть временными, а объекты могут переопределять стандартные методы.
Разве размеры различных примитивов не определены в спецификации Java? (§2.4.1)
Не в смысле, «сколько памяти она занимает», вот в чем вопрос. Только в смысле того, как они действуют. Например, байты, символы и шорты занимают все слово в стеке Java, даже если они работают с округлением и т. д.
Это похоже на измерение размера, как показано Хайнцем в его Информационном бюллетене № 78: javaspecialists.eu/archive/Issue078.html. Я использовал это. Его подход работает.
Вы должны измерить его с помощью инструмента или оценить вручную, и это зависит от используемой JVM.
Есть фиксированные накладные расходы на каждый объект. Это специфично для JVM, но я обычно оцениваю 40 байт. Затем вы должны посмотреть на членов класса. Ссылки на объекты занимают 4 (8) байта в 32-битной (64-битной) JVM. Примитивные типы:
Массивы подчиняются тем же правилам; то есть это ссылка на объект, которая занимает 4 (или 8) байта в вашем объекте, а затем его длина умножается на размер его элемента.
Попытка сделать это программно с вызовами Runtime.freeMemory() просто не даст вам большой точности из-за асинхронных вызовов сборщика мусора и т. д. Профилирование кучи с помощью -Xrunhprof или других инструментов даст вам наиболее точные результаты.
@erickson Я бы не был уверен в sizeof (boolean) == 1, глядя на этот поток (stackoverflow.com/questions/1907318/…). Не могли бы вы это прокомментировать?
@dma_k, в Java на самом деле нет настоящих логических значений. Размер логического значения составляет 4 байта вне массивов и 1 байт внутри boolean[]. На самом деле все примитивы, отличные от типов double / long, имеют размер 4 байта. Последние равны 8 (ответ также ошибочно ставит их как 4)
@bestsss: Точнее, минимальное выделение памяти зависит от платформы и реализации JVM. Также объекты в куче выровнены, поэтому после суммирования всех размеров нужно округлить.
Во-первых, «размер объекта» не является четко определенным понятием в Java. Вы можете иметь в виду сам объект, только с его членами, объектом и всеми объектами, на которые он ссылается (ссылочный граф). Вы могли иметь в виду размер в памяти или размер на диске. И JVM позволяет оптимизировать такие вещи, как строки.
Итак, единственный правильный способ - запросить JVM с хорошим профилировщиком (я использую YourKit), что, вероятно, не то, что вам нужно.
Однако из приведенного выше описания кажется, что каждая строка будет автономной и не будет иметь большого дерева зависимостей, поэтому метод сериализации, вероятно, будет хорошим приближением для большинства JVM. Проще всего это сделать следующим образом:
Serializable ser;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(ser);
oos.close();
return baos.size();
Помните, что если у вас есть объекты с общими ссылками, этот не буду дает правильный результат, и размер сериализации не всегда будет соответствовать размеру в памяти, но это хорошее приближение. Код будет немного более эффективным, если вы инициализируете размер ByteArrayOutputStream разумным значением.
Мне нравится такой подход. Насколько далеко вы были по размеру объекта?
Очень просто и эффективно. Другие методы слишком беспорядочные (особенно внутри Eclipse RCP). Спасибо.
Сериализация не отслеживает переходные переменные, а метод сериализации по умолчанию записывает строки в UTF-8, поэтому любые символы ANSI занимают только один байт. Если у вас много струн, ваш размер будет настолько мал, что станет бесполезным.
хотя это может не дать точного размера, для моих нужд мне нужно было только сравнение между двумя объектами, а SizeOf не инициализируется из веб-приложения. Спасибо!
Хорошая рекомендация YourKit. Другие альтернативы: VirtualVM и jvmmonitor.
Самое главное, ОП хочет знать размер своих Объектов, чтобы не попасть в потолок памяти. Я не считаю, что запись объектов в массив байтов (и, следовательно, удвоение потребления памяти) действительно помогает решить исходную проблему.
Сериализация не кажется хорошим предложением stackoverflow.com/a/52568/2859065
Несколько лет назад в Javaworld был статья об определении размера составных и потенциально вложенных Java-объектов, они в основном проходили через создание реализации sizeof () на Java. Подход в основном основан на другой работе, в которой люди экспериментально определили размер примитивов и типичных Java-объектов, а затем применили эти знания к методу, который рекурсивно просматривает граф объектов, чтобы подсчитать общий размер.
Он всегда будет несколько менее точным, чем нативная реализация C, просто из-за того, что происходит за кулисами класса, но это должен быть хорошим индикатором.
В качестве альтернативы проект SourceForge с соответствующим названием размер предлагает библиотеку Java5 с реализацией sizeof ().
P.S. Не используйте подход сериализации, нет корреляции между размером сериализованного объекта и объемом памяти, который он потребляет при работе.
Размер утилиты, вероятно, самый быстрый способ. В основном это то, что сказал Стефан, но уже упакованный в банку, готовую к употреблению.
ссылка в ответе не работает
Вы можете использовать пакет java.lang.instrument
Скомпилируйте и поместите этот класс в JAR:
import java.lang.instrument.Instrumentation;
public class ObjectSizeFetcher {
private static Instrumentation instrumentation;
public static void premain(String args, Instrumentation inst) {
instrumentation = inst;
}
public static long getObjectSize(Object o) {
return instrumentation.getObjectSize(o);
}
}
Добавьте к MANIFEST.MF следующее:
Premain-Class: ObjectSizeFetcher
Используйте getObjectSize:
public class C {
private int x;
private int y;
public static void main(String [] args) {
System.out.println(ObjectSizeFetcher.getObjectSize(new C()));
}
}
Вызов с помощью:
java -javaagent:ObjectSizeFetcherAgent.jar C
@Stefan Хороший намек! Подскажите, пожалуйста, какой будет размер byte[0], byte[1], byte[5], int[0], int[1], int[2] при описанном вами подходе? Было бы неплохо, если бы результаты включали накладные расходы на длину массива и выравнивание памяти.
Я попробовал это и получил странные и бесполезные результаты. Струн всегда было 32, независимо от размера. Я думал, что это, возможно, размер указателя, но для другого неизменяемого класса, который я создал, я получил 24. Он хорошо работает с примитивами, но тогда вам действительно не нужна программа, чтобы узнать, насколько велик char.
@Brel это решение является лишь «приблизительным значением объема памяти, потребляемого указанным объектом», как указано в документации. Также я предполагаю, что авторы решили установить размер String как 32 байта (только указатель?) Из-за пула Java String, что затрудняет определение того, является ли экземпляр String общим (хранится в пуле) или местный и уникальный для класса.
Как я могу использовать ObjectSizeFetcher, если не экспортирую jar? У меня есть тестовый Java-проект в eclipse.
что, если я использую tomcat, это то же самое? Когда я добавляю Premain-Class: ObjectSizeFetcher в MANIFEST.MF в webcontent-> meta-inf-> manifest.mf, и я запускаю планировщик, чтобы проверить свой размер объекта в ecplise, за исключением «Незаконный доступ: это экземпляр веб-приложения уже остановлен. Не удалось загрузить wodinow.weixin.jaskey.util.ObjectSizeFetcher. Выдается исключение java.lang.IllegalStateException
Каким будет размер объекта, если он содержит две карты, так что содержимое первой карты вставляется во вторую карту как есть путем итерации по первой карте, то есть map2.put (map1.key1, map1.value1) , map2.put (map1.key2, map1.value2) .. и так далее. Я попробовал это, и похоже, что программа написана таким образом, что она вычисляет размер обеих карт, хотя вторая карта содержит только ссылки на содержимое первой карты.
Откуда берется ObjectSizeFetcherAgent.jar?
byte[] members count only as a single pointer.
Эти результаты кажутся далекими. Я десериализовал большой CSV в List <List <String>>. Прокрутил каждую деталь и просуммировал размер. ObjectSizeOf сообщил о 7,5 МБ. Исходный файл был 119,6 МБ! Объем используемой памяти увеличился на 154 МБ, когда я загрузил файл после цикла вызовов GC с паузами между ними. Не знаю, как Instrumentation делает свое дело, но вы можете проверить полученные цифры.
Должно быть что-то не так с этим подходом при попытке определить размер моего веб-сеанса: он сообщает 40, в то время как ObjectGraphMeasurer (github.com/DimitrisAndreou/memory-measurer) сообщает «Footprint {Objects = 34, References = 52, Primitives = [float, int x 19, char x 257, длинный x 2, байт x 68557]} ". ObjectGraphMeasurer явно победитель.
для тех, кто забыл, чтобы добавить манифест в банку, используйте jar -cvfm ObjectSizeFetcherAgent.jar mymanifest.txt C.class заполните mymanifest.txt тем, что вам нужно, например, Premain-Class
@YShinkarev В документации не описан способ сделать это без файла jar. Только этот "-javaagent: jarpath [= options]"
JDK содержит пакет java.lang.instrument начиная с jdk 1.5
Каковы побочные эффекты при запуске JVM с прикрепленным этим инструментальным агентом? Влияние на производительность? Влияние на память?
@brel Причина, по которой String имеет размер всего 32 байта независимо от фактической длины, заключается в том, что часть переменной длины строки хранится в char [], который является ее собственным объектом. Чтобы получить истинный размер объекта, вам нужно добавить размер самого объекта и размер каждого объекта, на который он ссылается.
Еще один хороший пример: baeldung.com/java-size-of-object
Класс java.lang.instrument.Instrumentation предоставляет удобный способ получить размер объекта Java, но требует, чтобы вы определяли premain и запускали вашу программу с помощью агента Java. Это очень утомительно, когда вам не нужен какой-либо агент, а вам нужно предоставить фиктивный агент Jar для вашего приложения.
Так что я получил альтернативное решение, использующее класс Unsafe от sun.misc. Итак, учитывая выравнивание кучи объектов в соответствии с архитектурой процессора и вычисляя максимальное смещение поля, вы можете измерить размер объекта Java. В приведенном ниже примере я использую вспомогательный класс UtilUnsafe для получения ссылки на объект sun.misc.Unsafe.
private static final int NR_BITS = Integer.valueOf(System.getProperty("sun.arch.data.model"));
private static final int BYTE = 8;
private static final int WORD = NR_BITS/BYTE;
private static final int MIN_SIZE = 16;
public static int sizeOf(Class src){
//
// Get the instance fields of src class
//
List<Field> instanceFields = new LinkedList<Field>();
do{
if (src == Object.class) return MIN_SIZE;
for (Field f : src.getDeclaredFields()) {
if ((f.getModifiers() & Modifier.STATIC) == 0){
instanceFields.add(f);
}
}
src = src.getSuperclass();
}while(instanceFields.isEmpty());
//
// Get the field with the maximum offset
//
long maxOffset = 0;
for (Field f : instanceFields) {
long offset = UtilUnsafe.UNSAFE.objectFieldOffset(f);
if (offset > maxOffset) maxOffset = offset;
}
return (((int)maxOffset/WORD) + 1)*WORD;
}
class UtilUnsafe {
public static final sun.misc.Unsafe UNSAFE;
static {
Object theUnsafe = null;
Exception exception = null;
try {
Class<?> uc = Class.forName("sun.misc.Unsafe");
Field f = uc.getDeclaredField("theUnsafe");
f.setAccessible(true);
theUnsafe = f.get(uc);
} catch (Exception e) { exception = e; }
UNSAFE = (sun.misc.Unsafe) theUnsafe;
if (UNSAFE == null) throw new Error("Could not obtain access to sun.misc.Unsafe", exception);
}
private UtilUnsafe() { }
}
Интересный подход, но не предполагает ли это, что объект и хранилище его полей не фрагментированы?
Да, и я не знаю какой-либо реализации JVM, которая делает такую фрагментацию.
Я не понимаю. Фрагментация не вариант :) Возьмем, к примеру, объект C, который хранится как поле объектов A и B. Разве это не сдвигает все это в A или B?
Извините, но я не понимаю ни одну вашу точку зрения. Согласно моей интерпретации, объекты в Java не могут храниться внутри других объектов, как это происходит со структурами C или типами значений в .Net. Поэтому, когда вы говорите: «объект C, который хранится как поле объектов A и B», это означает, что объекты A и B имеют поля, в которых хранятся ссылки (указатели) на объект C. Тогда размер A и B равен смещение этого поля плюс размер ссылки (указателя) на объект C. А размер ссылки равен размеру одного слова.
Да ладно, мы говорим о мелких размерах. Виноват.
К сожалению, sun.misc.Unsafe не поддерживается в JVM Google App Engine! Проект не компилируется со ссылкой :(
Хардкорный метод Unsafe упоминается по адресу: highscalable.wordpress.com/2012/02/02/…: он "просто" читает внутреннее поле длины объекта.
Вы можете создать дамп кучи (например, с помощью jmap), а затем проанализировать вывод, чтобы найти размеры объектов. Это автономное решение, но вы можете изучить мелкие и глубокие размеры и т. д.
Существует также инструмент Измеритель памяти (ранее на Код Google, теперь на GitHub), который прост и опубликован под коммерчески удобным Лицензия Apache 2.0, как обсуждалось в аналогичный вопрос.
Он также требует аргумента командной строки для интерпретатора java, если вы хотите измерить потребление байтов памяти, но в остальном, похоже, работает нормально, по крайней мере, в сценариях, которые я использовал.
Однажды я написал быстрый тест, чтобы оценить на лету:
public class Test1 {
// non-static nested
class Nested { }
// static nested
static class StaticNested { }
static long getFreeMemory () {
// waits for free memory measurement to stabilize
long init = Runtime.getRuntime().freeMemory(), init2;
int count = 0;
do {
System.out.println("waiting..." + init);
System.gc();
try { Thread.sleep(250); } catch (Exception x) { }
init2 = init;
init = Runtime.getRuntime().freeMemory();
if (init == init2) ++ count; else count = 0;
} while (count < 5);
System.out.println("ok..." + init);
return init;
}
Test1 () throws InterruptedException {
Object[] s = new Object[10000];
Object[] n = new Object[10000];
Object[] t = new Object[10000];
long init = getFreeMemory();
//for (int j = 0; j < 10000; ++ j)
// s[j] = new Separate();
long afters = getFreeMemory();
for (int j = 0; j < 10000; ++ j)
n[j] = new Nested();
long aftersn = getFreeMemory();
for (int j = 0; j < 10000; ++ j)
t[j] = new StaticNested();
long aftersnt = getFreeMemory();
System.out.println("separate: " + -(afters - init) + " each = " + -(afters - init) / 10000);
System.out.println("nested: " + -(aftersn - afters) + " each = " + -(aftersn - afters) / 10000);
System.out.println("static nested: " + -(aftersnt - aftersn) + " each = " + -(aftersnt - aftersn) / 10000);
}
public static void main (String[] args) throws InterruptedException {
new Test1();
}
}
Общая концепция заключается в размещении объектов и измерении изменений в свободном пространстве кучи. Ключ - getFreeMemory(), который запрашивает запуск GC и ожидает стабилизации сообщенного размера свободной кучи. Результат вышеизложенного:
nested: 160000 each=16
static nested: 160000 each=16
Это то, что мы ожидаем, учитывая поведение выравнивания и возможные накладные расходы заголовка блока кучи.
Метод измерения, подробно описанный в принятом ответе, здесь самый точный. Описанный мной метод является точным, но только в контролируемых условиях, когда никакие другие потоки не создают / не удаляют объекты.
long heapSizeBefore = Runtime.getRuntime().totalMemory();
// Code for object construction
...
long heapSizeAfter = Runtime.getRuntime().totalMemory();
long size = heapSizeAfter - heapSizeBefore;
size дает вам увеличение использования памяти jvm из-за создания объекта, и это обычно размер объекта.
что, если сборщик мусора работает посередине во время // кода для создания объекта? Теперь может всегда давать правильный результат.
Вот утилита, которую я сделал, используя некоторые из связанных примеров, для обработки 32-разрядных, 64-разрядных и 64-разрядных версий со сжатым ООП. Он использует sun.misc.Unsafe.
Он использует Unsafe.addressSize() для получения размера собственного указателя и Unsafe.arrayIndexScale( Object[].class ) для размера ссылки Java.
Он использует смещение поля известного класса для определения базового размера объекта.
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.IdentityHashMap;
import java.util.Stack;
import sun.misc.Unsafe;
/** Usage:
* MemoryUtil.sizeOf( object )
* MemoryUtil.deepSizeOf( object )
* MemoryUtil.ADDRESS_MODE
*/
public class MemoryUtil
{
private MemoryUtil()
{
}
public static enum AddressMode
{
/** Unknown address mode. Size calculations may be unreliable. */
UNKNOWN,
/** 32-bit address mode using 32-bit references. */
MEM_32BIT,
/** 64-bit address mode using 64-bit references. */
MEM_64BIT,
/** 64-bit address mode using 32-bit compressed references. */
MEM_64BIT_COMPRESSED_OOPS
}
/** The detected runtime address mode. */
public static final AddressMode ADDRESS_MODE;
private static final Unsafe UNSAFE;
private static final long ADDRESS_SIZE; // The size in bytes of a native pointer: 4 for 32 bit, 8 for 64 bit
private static final long REFERENCE_SIZE; // The size of a Java reference: 4 for 32 bit, 4 for 64 bit compressed oops, 8 for 64 bit
private static final long OBJECT_BASE_SIZE; // The minimum size of an Object: 8 for 32 bit, 12 for 64 bit compressed oops, 16 for 64 bit
private static final long OBJECT_ALIGNMENT = 8;
/** Use the offset of a known field to determine the minimum size of an object. */
private static final Object HELPER_OBJECT = new Object() { byte b; };
static
{
try
{
// Use reflection to get a reference to the 'Unsafe' object.
Field f = Unsafe.class.getDeclaredField( "theUnsafe" );
f.setAccessible( true );
UNSAFE = (Unsafe) f.get( null );
OBJECT_BASE_SIZE = UNSAFE.objectFieldOffset( HELPER_OBJECT.getClass().getDeclaredField( "b" ) );
ADDRESS_SIZE = UNSAFE.addressSize();
REFERENCE_SIZE = UNSAFE.arrayIndexScale( Object[].class );
if ( ADDRESS_SIZE == 4 )
{
ADDRESS_MODE = AddressMode.MEM_32BIT;
}
else if ( ADDRESS_SIZE == 8 && REFERENCE_SIZE == 8 )
{
ADDRESS_MODE = AddressMode.MEM_64BIT;
}
else if ( ADDRESS_SIZE == 8 && REFERENCE_SIZE == 4 )
{
ADDRESS_MODE = AddressMode.MEM_64BIT_COMPRESSED_OOPS;
}
else
{
ADDRESS_MODE = AddressMode.UNKNOWN;
}
}
catch( Exception e )
{
throw new Error( e );
}
}
/** Return the size of the object excluding any referenced objects. */
public static long shallowSizeOf( final Object object )
{
Class<?> objectClass = object.getClass();
if ( objectClass.isArray() )
{
// Array size is base offset + length * element size
long size = UNSAFE.arrayBaseOffset( objectClass )
+ UNSAFE.arrayIndexScale( objectClass ) * Array.getLength( object );
return padSize( size );
}
else
{
// Object size is the largest field offset padded out to 8 bytes
long size = OBJECT_BASE_SIZE;
do
{
for( Field field : objectClass.getDeclaredFields() )
{
if ( (field.getModifiers() & Modifier.STATIC) == 0 )
{
long offset = UNSAFE.objectFieldOffset( field );
if ( offset >= size )
{
size = offset + 1; // Field size is between 1 and PAD_SIZE bytes. Padding will round up to padding size.
}
}
}
objectClass = objectClass.getSuperclass();
}
while( objectClass != null );
return padSize( size );
}
}
private static final long padSize( final long size )
{
return (size + (OBJECT_ALIGNMENT - 1)) & ~(OBJECT_ALIGNMENT - 1);
}
/** Return the size of the object including any referenced objects. */
public static long deepSizeOf( final Object object )
{
IdentityHashMap<Object,Object> visited = new IdentityHashMap<Object,Object>();
Stack<Object> stack = new Stack<Object>();
if ( object != null ) stack.push( object );
long size = 0;
while( !stack.isEmpty() )
{
size += internalSizeOf( stack.pop(), stack, visited );
}
return size;
}
private static long internalSizeOf( final Object object, final Stack<Object> stack, final IdentityHashMap<Object,Object> visited )
{
// Scan for object references and add to stack
Class<?> c = object.getClass();
if ( c.isArray() && !c.getComponentType().isPrimitive() )
{
// Add unseen array elements to stack
for( int i = Array.getLength( object ) - 1; i >= 0; i-- )
{
Object val = Array.get( object, i );
if ( val != null && visited.put( val, val ) == null )
{
stack.add( val );
}
}
}
else
{
// Add unseen object references to the stack
for( ; c != null; c = c.getSuperclass() )
{
for( Field field : c.getDeclaredFields() )
{
if ( (field.getModifiers() & Modifier.STATIC) == 0
&& !field.getType().isPrimitive() )
{
field.setAccessible( true );
try
{
Object val = field.get( object );
if ( val != null && visited.put( val, val ) == null )
{
stack.add( val );
}
}
catch( IllegalArgumentException e )
{
throw new RuntimeException( e );
}
catch( IllegalAccessException e )
{
throw new RuntimeException( e );
}
}
}
}
}
return shallowSizeOf( object );
}
}
Вы тестировали этот класс со значениями? Я пробовал, но для меня неверные значения !!!.
Значения, которые он дал мне для простого объекта, были примерно правильными, но с разницей в 10 раз для списка, содержащего 1 миллион объектов. Тем не менее, очень хорошая работа!
Интересно. Я тестировал его с помощью JDK7u67 в Windows 7 x64 и Linux 2.6.16 / x86_64, используя каждый из режимов адресации 32/64 / oop. Я сравнил это с дампами памяти, проанализированными в Eclipse Memory Analyzer 1.3.x. Какую установку вы используете? У вас есть конкретный пример, который я мог бы попробовать?
Лучший выбор, который я могу сделать. Я не могу использовать Instrumentation, потому что я не запускаю tomcat, ObjectSizeCalculator, потому что не уверен в типе виртуальной машины (HotSpot) и Spring beans в JOL. Я использую это и добавляю второй параметр для игнорирования синглтонов, а именно AbstractRefreshableApplicationContext.getBeanFactory().getSingletonMutex(), и рефакторинг кода internalSizeOf, чтобы игнорировать Class и Enum.
Для сравнения результатов используйте ObjectSizeCalculator (вычислить весь сервер от 1 ГБ до 10 секунд). JOL вызывает MemError (6 ГБ не может быть выделено), и я не получаю таких же результатов, вероятно, из-за перечислений.
Мой ответ основан на коде, предоставленном Ником. Этот код измеряет общее количество байтов, которые занимают сериализованный объект. Таким образом, это фактически измеряет материал сериализации + объем памяти простого объекта (просто сериализуйте, например, int, и вы увидите, что общее количество сериализованных байтов не соответствует 4). Поэтому, если вы хотите получить необработанный номер байта, используемый именно для вашего объекта, вам нужно немного изменить этот код. Вот так:
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class ObjectSizeCalculator {
private Object getFirstObjectReference(Object o) {
String objectType = o.getClass().getTypeName();
if (objectType.substring(objectType.length()-2).equals("[]")) {
try {
if (objectType.equals("java.lang.Object[]"))
return ((Object[])o)[0];
else if (objectType.equals("int[]"))
return ((int[])o)[0];
else
throw new RuntimeException("Not Implemented !");
} catch (IndexOutOfBoundsException e) {
return null;
}
}
return o;
}
public int getObjectSizeInBytes(Object o) {
final String STRING_JAVA_TYPE_NAME = "java.lang.String";
if (o == null)
return 0;
String objectType = o.getClass().getTypeName();
boolean isArray = objectType.substring(objectType.length()-2).equals("[]");
Object objRef = getFirstObjectReference(o);
if (objRef != null && !(objRef instanceof Serializable))
throw new RuntimeException("Object must be serializable for measuring it's memory footprint using this method !");
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(o);
oos.close();
byte[] bytes = baos.toByteArray();
for (int i = bytes.length - 1, j = 0; i != 0; i--, j++) {
if (objectType != STRING_JAVA_TYPE_NAME) {
if (bytes[i] == 112)
if (isArray)
return j - 4;
else
return j;
} else {
if (bytes[i] == 0)
return j - 1;
}
}
} catch (Exception e) {
return -1;
}
return -1;
}
}
Я тестировал это решение с примитивными типами, String и некоторыми тривиальными классами. Также могут быть не охваченные случаи.
Обновлено: Пример, измененный для поддержки вычисления объема памяти для объектов массива.
Этот ответ не связан с размером объекта, но когда вы используете массив для размещения объектов; какой объем памяти будет выделен для объекта.
Таким образом, массивы, список или сопоставление всей этой коллекции на самом деле не будут хранить объекты (только во время примитивов, требуется реальный размер памяти объекта), он будет хранить только ссылки для этих объектов.
Теперь Used heap memory = sizeOfObj + sizeOfRef (* 4 bytes) in collection
ПРИМИТИВЫ
int [] intArray = new int [1]; will require 4 bytes.
long [] longArray = new long [1]; will require 8 bytes.
ОБЪЕКТЫ
Object[] objectArray = new Object[1]; will require 4 bytes. The object can be any user defined Object.
Long [] longArray = new Long [1]; will require 4 bytes.
Я хочу сказать, что для всего объекта REFERENCE требуется всего 4 байта памяти. Это может быть ссылка на строку ИЛИ двойная ссылка на объект, но в зависимости от создания объекта необходимая память будет отличаться.
например) Если я создам объект для класса ниже ReferenceMemoryTest, то будет создано 4 + 4 + 4 = 12 байт памяти. Память может отличаться, когда вы пытаетесь инициализировать ссылки.
class ReferenceMemoryTest {
public String refStr;
public Object refObj;
public Double refDoub;
}
Поэтому при создании массива объектов / ссылок все его содержимое будет занято ссылками NULL. И мы знаем, что для каждой ссылки требуется 4 байта.
И, наконец, выделение памяти для приведенного ниже кода составляет 20 байтов.
ReferenceMemoryTest ref1 = новый ReferenceMemoryTest (); (4 (ref1) + 12 = 16 байт) ReferenceMemoryTest ref2 = ref1; (4 (ref2) + 16 = 20 байт)
Как может 4-байтовое целое число и ссылка на объект неизвестного размера уместиться в 4 байта?
@EJP Я хочу сказать, что для всего объекта REFERENCE требуется всего 4 байта памяти. Это может быть ссылка на строку ИЛИ двойная ссылка на объект, но в зависимости от создания объекта необходимая память будет отличаться.
Многие другие ответы предоставляют мелкие размеры - например, размер HashMap без каких-либо ключей или значений, что, вероятно, не то, что вам нужно.
В проекте jamm используется указанный выше пакет java.lang.instrumentation, но он обходит дерево и поэтому может дать вам глубокое использование памяти.
new MemoryMeter().measureDeep(myHashMap);
https://github.com/jbellis/jamm
To use MemoryMeter, start the JVM with "-javaagent:/jamm.jar"
Без необходимости возиться с инструментами и т. д., И если вам не нужно знать точный размер объекта в байтах, вы можете использовать следующий подход:
System.gc();
Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
do your job here
System.gc();
Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
Таким образом, вы читаете использованную память до и после, и вызывая сборщик мусора непосредственно перед получением использованной памяти, вы снижаете «шум» почти до 0.
Для более надежного результата вы можете запустить задание n раз, а затем разделить используемую память на n, получив, сколько памяти занимает одно выполнение. Более того, вы можете запустить все это несколько раз и получить среднее значение.
Разве System.gc() не просто уведомляет о том, что вы хотите выполнить сборку мусора? Не гарантируется, что сборщик мусора вообще будет вызван.
@действительно мило. Это небезопасно, потому что вы никогда не сможете, что GC делает или влияет на память между вашими строками. Таким образом, "между" двумя методами freeMemory GC может освободить больше места, которое вы не учитываете, поэтому ваш объект будет выглядеть меньше
@MertSerimer «небезопасно» для меня находится на совершенно другом уровне: в лучшем случае это не так точно, как я также сказал. Кроме того, вы не можете управлять GC (как заявил Raildex), но и для этого случая я предложил вставить его в цикл. Это просто быстрая, грязная и приблизительная система, которая работает, если результат не должен быть очень надежным, как указано.
С этим есть много проблем, но это дает вам хорошую добычу.
Еще когда я работал в Twitter, я написал утилиту для расчета размера глубоких объектов. Он учитывает разные модели памяти (32-битную, сжатую, 64-битную), заполнение, заполнение подкласса, корректно работает с круговыми структурами данных и массивами. Вы можете просто скомпилировать этот файл .java; у него нет внешних зависимостей:
Зя! Я также хотел бы отметить ваш презентация: слайды 15-20 отлично подходят для того, чтобы помочь инстинктивно почувствовать стоимость различных решений по структуре данных. Спасибо, что разместили это!
«у него нет внешних зависимостей» - с каких это пор гуава не является внешней зависимостью?
очень похоже на github.com/JetBrains/jdk8u_nashorn/blob/master/src/jdk/nasho rn /…? : O
Гуава - это внешняя зависимость.
Вам следует использовать веселье, инструмент, разработанный как часть проекта OpenJDK.
JOL (Java Object Layout) is the tiny toolbox to analyze object layout schemes in JVMs. These tools are using Unsafe, JVMTI, and Serviceability Agent (SA) heavily to decoder the actual object layout, footprint, and references. This makes JOL much more accurate than other tools relying on heap dumps, specification assumptions, etc.
Чтобы получить размеры примитивов, ссылок и элементов массива, используйте VMSupport.vmDetails(). В Oracle JDK 1.8.0_40, работающем в 64-битной Windows (используется во всех следующих примерах), этот метод возвращает
Running 64-bit HotSpot VM.
Using compressed oop with 0-bit shift.
Using compressed klass with 3-bit shift.
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]
Вы можете получить небольшой размер экземпляра объекта с помощью ClassLayout.parseClass(Foo.class).toPrintable() (необязательно, передав экземпляр в toPrintable). Это только пространство, занимаемое одним экземпляром этого класса; он не включает никаких других объектов, на которые ссылается этот класс. делает включает служебные данные виртуальной машины для заголовка объекта, выравнивания полей и заполнения. Для java.util.regex.Pattern:
java.util.regex.Pattern object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (0000 0001 0000 0000 0000 0000 0000 0000)
4 4 (object header) 00 00 00 00 (0000 0000 0000 0000 0000 0000 0000 0000)
8 4 (object header) cb cf 00 20 (1100 1011 1100 1111 0000 0000 0010 0000)
12 4 int Pattern.flags 0
16 4 int Pattern.capturingGroupCount 1
20 4 int Pattern.localCount 0
24 4 int Pattern.cursor 48
28 4 int Pattern.patternLength 0
32 1 boolean Pattern.compiled true
33 1 boolean Pattern.hasSupplementary false
34 2 (alignment/padding gap) N/A
36 4 String Pattern.pattern (object)
40 4 String Pattern.normalizedPattern (object)
44 4 Node Pattern.root (object)
48 4 Node Pattern.matchRoot (object)
52 4 int[] Pattern.buffer null
56 4 Map Pattern.namedGroups null
60 4 GroupHead[] Pattern.groupNodes null
64 4 int[] Pattern.temp null
68 4 (loss due to the next object alignment)
Instance size: 72 bytes (reported by Instrumentation API)
Space losses: 2 bytes internal + 4 bytes external = 6 bytes total
Вы можете получить сводное представление о глубоком размере экземпляра объекта с помощью GraphLayout.parseInstance(obj).toFootprint(). Конечно, некоторые объекты в посадочном месте могут быть совместно используемыми (на которые также ссылаются другие объекты), так что это избыточное приближение к пространству, которое может быть освобождено, когда этот объект будет собран сборщиком мусора. Для результата Pattern.compile("^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$") (взятого из этот ответ) jol сообщает общий размер 1840 байтов, из которых только 72 являются экземпляром Pattern.
java.util.regex.Pattern instance footprint:
COUNT AVG SUM DESCRIPTION
1 112 112 [C
3 272 816 [Z
1 24 24 java.lang.String
1 72 72 java.util.regex.Pattern
9 24 216 java.util.regex.Pattern
13 24 312 java.util.regex.Pattern
1 16 16 java.util.regex.Pattern$Begin
3 24 72 java.util.regex.Pattern$BitClass
3 32 96 java.util.regex.Pattern$Curly
1 24 24 java.util.regex.Pattern$Dollar
1 16 16 java.util.regex.Pattern$LastNode
1 16 16 java.util.regex.Pattern$Node
2 24 48 java.util.regex.Pattern$Single
40 1840 (total)
Если вы вместо этого используете GraphLayout.parseInstance(obj).toPrintable(), jol сообщит вам адрес, размер, тип, значение и путь разыменования полей для каждого объекта, на который имеется ссылка, хотя обычно это слишком много деталей, чтобы быть полезным. Для текущего примера шаблона вы можете получить следующее. (Адреса, скорее всего, будут меняться между запусками.)
java.util.regex.Pattern object externals:
ADDRESS SIZE TYPE PATH VALUE
d5e5f290 16 java.util.regex.Pattern$Node .root.next.atom.next (object)
d5e5f2a0 120 (something else) (somewhere else) (something else)
d5e5f318 16 java.util.regex.Pattern$LastNode .root.next.next.next.next.next.next.next (object)
d5e5f328 21664 (something else) (somewhere else) (something else)
d5e647c8 24 java.lang.String .pattern (object)
d5e647e0 112 [C .pattern.value [^, [, a, -, z, A, -, Z, 0, -, 9, _, ., +, -, ], +, @, [, a, -, z, A, -, Z, 0, -, 9, -, ], +, \, ., [, a, -, z, A, -, Z, 0, -, 9, -, ., ], +, $]
d5e64850 448 (something else) (somewhere else) (something else)
d5e64a10 72 java.util.regex.Pattern (object)
d5e64a58 416 (something else) (somewhere else) (something else)
d5e64bf8 16 java.util.regex.Pattern$Begin .root (object)
d5e64c08 24 java.util.regex.Pattern$BitClass .root.next.atom.val$rhs (object)
d5e64c20 272 [Z .root.next.atom.val$rhs.bits [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false]
d5e64d30 24 java.util.regex.Pattern .root.next.atom.val$lhs.val$lhs.val$lhs.val$lhs.val$lhs.val$lhs (object)
d5e64d48 24 java.util.regex.Pattern .root.next.atom.val$lhs.val$lhs.val$lhs.val$lhs.val$lhs.val$rhs (object)
d5e64d60 24 java.util.regex.Pattern .root.next.atom.val$lhs.val$lhs.val$lhs.val$lhs.val$lhs (object)
d5e64d78 24 java.util.regex.Pattern .root.next.atom.val$lhs.val$lhs.val$lhs.val$lhs.val$rhs (object)
d5e64d90 24 java.util.regex.Pattern .root.next.atom.val$lhs.val$lhs.val$lhs.val$lhs (object)
d5e64da8 24 java.util.regex.Pattern .root.next.atom.val$lhs.val$lhs.val$lhs (object)
d5e64dc0 24 java.util.regex.Pattern .root.next.atom.val$lhs.val$lhs (object)
d5e64dd8 24 java.util.regex.Pattern .root.next.atom.val$lhs (object)
d5e64df0 24 java.util.regex.Pattern .root.next.atom (object)
d5e64e08 32 java.util.regex.Pattern$Curly .root.next (object)
d5e64e28 24 java.util.regex.Pattern$Single .root.next.next (object)
d5e64e40 24 java.util.regex.Pattern$BitClass .root.next.next.next.atom.val$rhs (object)
d5e64e58 272 [Z .root.next.next.next.atom.val$rhs.bits [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false]
d5e64f68 24 java.util.regex.Pattern .root.next.next.next.atom.val$lhs.val$lhs.val$lhs (object)
d5e64f80 24 java.util.regex.Pattern .root.next.next.next.atom.val$lhs.val$lhs.val$rhs (object)
d5e64f98 24 java.util.regex.Pattern .root.next.next.next.atom.val$lhs.val$lhs (object)
d5e64fb0 24 java.util.regex.Pattern .root.next.next.next.atom.val$lhs.val$rhs (object)
d5e64fc8 24 java.util.regex.Pattern .root.next.next.next.atom.val$lhs (object)
d5e64fe0 24 java.util.regex.Pattern .root.next.next.next.atom (object)
d5e64ff8 32 java.util.regex.Pattern$Curly .root.next.next.next (object)
d5e65018 24 java.util.regex.Pattern$Single .root.next.next.next.next (object)
d5e65030 24 java.util.regex.Pattern$BitClass .root.next.next.next.next.next.atom.val$rhs (object)
d5e65048 272 [Z .root.next.next.next.next.next.atom.val$rhs.bits [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false]
d5e65158 24 java.util.regex.Pattern .root.next.next.next.next.next.atom.val$lhs.val$lhs.val$lhs.val$lhs (object)
d5e65170 24 java.util.regex.Pattern .root.next.next.next.next.next.atom.val$lhs.val$lhs.val$lhs.val$rhs (object)
d5e65188 24 java.util.regex.Pattern .root.next.next.next.next.next.atom.val$lhs.val$lhs.val$lhs (object)
d5e651a0 24 java.util.regex.Pattern .root.next.next.next.next.next.atom.val$lhs.val$lhs.val$rhs (object)
d5e651b8 24 java.util.regex.Pattern .root.next.next.next.next.next.atom.val$lhs.val$lhs (object)
d5e651d0 24 java.util.regex.Pattern .root.next.next.next.next.next.atom.val$lhs (object)
d5e651e8 24 java.util.regex.Pattern .root.next.next.next.next.next.atom (object)
d5e65200 32 java.util.regex.Pattern$Curly .root.next.next.next.next.next (object)
d5e65220 120 (something else) (somewhere else) (something else)
d5e65298 24 java.util.regex.Pattern$Dollar .root.next.next.next.next.next.next (object)
Записи "(что-то еще)" описывать другие объекты в куче, которые не являются частью этого графа объектов.
Лучшая документация jol - это jol образцы в репозитории jol. Примеры демонстрируют общие операции jol и показывают, как вы можете использовать jol для анализа внутренних компонентов виртуальной машины и сборщика мусора.
Этот ответ должен иметь больше голосов. Определенно очень хороший вариант для проверки. Обновлено: Проверено, что это было добавлено в этом году, а вопрос был задан в '08. Вероятно, лучший и самый простой вариант сделать то, что OP просил на данный момент.
Автор инструмента написал сообщение в блоге о Jol.
Для определения размера объекта "obj" используйте: org.openjdk.jol.info.GraphLayout.parseInstance (obj) .tota lSize ();
Обратите внимание, что vmDetails теперь VM.current().details().
Ознакомьтесь с GraphLayout.parseInstance(instance).toFootprint(). Мне показалось более полезным разбираться в размерах объектов.
Я случайно нашел класс java "jdk.nashorn.internal.ir.debug.ObjectSizeCalculator", уже в jdk, который прост в использовании и кажется весьма полезным для определения размера объекта.
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
То же самое, я пробовал другие решения, предложенные выше, и наткнулся на ObjectSizeCalculator. Я полагаю, что никто не упоминал об этом раньше, поскольку он был недавно представлен в JDK 8 как часть проекта Nashorn. Однако я не нашел официальной документации по этому классу в Интернете.
Кажется, это не учитывает длину строк. Это примерно размер в стеке?
У меня есть хэш-карта, где com.carrotsearch.RamUsageEstimator возвращает примерно половину ObjectSizeCalculator. Какая из них правда? - Какой из них надежнее?
Обратите внимание, что ObjectSizeCalculator поддерживается только на виртуальной машине HotSpot.
Также jdk.nashorn.internal.ir.debug.ObjectSizeCalculator больше не присутствует в JDK 11
Для JSONObject вам может помочь приведенный ниже код.
`JSONObject.toString().getBytes("UTF-8").length`
возвращает размер в байтах
Я проверил это с помощью объекта JSONArray, записав его в файл. Он дает размер объекта.
это будет работать только для объектов, которые в основном являются строками.
Просто используйте визуальную виртуальную машину Java.
В нем есть все необходимое для профилирования и устранения проблем с памятью.
Он также имеет консоль OQL (Object Query Language), которая позволяет делать много полезных вещей, одна из которых - sizeof(o).
Предположим, я объявляю класс с именем Complex следующим образом:
public class Complex {
private final long real;
private final long imaginary;
// omitted
}
Чтобы увидеть, сколько памяти выделено живым экземплярам этого класса:
$ jmap -histo:live <pid> | grep Complex
num #instances #bytes class name (module)
-------------------------------------------------------
327: 1 32 Complex
Я искал расчет размера объекта во время выполнения, который отвечал бы следующим требованиям:
Следующее основано на основном коде исходной статьи специалистов по Java (https://www.javaspecialists.eu/archive/Issue078.html) и нескольких битах из небезопасной версии в другом ответе на этот вопрос.
Надеюсь, кому-то это пригодится.
public class JavaSize {
private static final int NR_BITS = Integer.valueOf(System.getProperty("sun.arch.data.model"));
private static final int BYTE = 8;
private static final int WORD = NR_BITS / BYTE;
private static final int HEADER_SIZE = 8;
public static int sizeOf(Class<?> clazz) {
int result = 0;
while (clazz != null) {
Field[] fields = clazz.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
if (!Modifier.isStatic(fields[i].getModifiers())) {
if (fields[i].getType().isPrimitive()) {
Class<?> primitiveClass = fields[i].getType();
if (primitiveClass == boolean.class || primitiveClass == byte.class) {
result += 1;
} else if (primitiveClass == short.class) {
result += 2;
} else if (primitiveClass == int.class || primitiveClass == float.class) {
result += 4;
} else if (primitiveClass == double.class || primitiveClass == long.class) {
result += 8;
}
} else {
// assume compressed references.
result += 4;
}
}
}
clazz = clazz.getSuperclass();
// round up to the nearest WORD length.
if ((result % WORD) != 0) {
result += WORD - (result % WORD);
}
}
result += HEADER_SIZE;
return result;
}
}
При использовании JetBrains IntelliJ сначала включите «Присоединить агент памяти» в меню Файл | Настройки | Сборка, выполнение, развертывание | Отладчик.
При отладке щелкните правой кнопкой мыши интересующую переменную и выберите «Рассчитать сохраненный размер»: 
Я не вижу этого в моем Intellij - используя 2019.2. Какую версию ты используешь?
Я добавил своего Агента с конфигами mvn и объяснил, как здесь: stackoverflow.com/a/36102269/711855