Чи існує в Java вбудований метод для визначення розміру будь-якого типу даних? Чи є спосіб знайти розмір?
Чи існує в Java вбудований метод для визначення розміру будь-якого типу даних? Чи є спосіб знайти розмір?
Відповіді:
Ні. У стандартній бібліотеці класів Java SE такого методу немає.
Думка дизайнерів полягає в тому, що вона не потрібна в Java, оскільки мова позбавляє потреби в застосунку 1 знати про те, скільки місця потрібно зарезервувати для примітивного значення, об'єкта або масиву із заданою кількістю елементів.
Ви можете подумати, що оператор sizeof був би корисний для людей, які повинні знати, скільки місця займають їхні структури даних. Однак ви також можете отримати цю інформацію та багато іншого, просто і надійно, використовуючи профайлер пам'яті Java, тому немає необхідності в методі sizeof.
Попередні коментатори висловлювали думку, яка sizeof(someType)була б більш читабельною, ніж 4. Якщо ви приймаєте цей аргумент читабельності, тоді засіб у ваших руках. Просто визначте такий клас ...
public class PrimitiveSizes {
public static int sizeof(byte b) { return 1; }
public static int sizeof(short s) { return 2; }
// etcetera
}
... і статично імпортувати його ...
import static PrimitiveSizes.*;
Або визначте деякі іменовані константи; напр
public static final int SIZE_OF_INT = 4;
Або (Java 8 та пізніші версії) використовують Integer.BYTESконстанту тощо.
Чому дизайнери Java не впровадили це в стандартні бібліотеки? Я припускаю, що:
Існує також проблема, що наступним попитом буде sizeof(Object o)метод, який загрожує технічними труднощами.
Ключове слово в наведеному вище - це "вони"!
1 - Можливо, програмісту доведеться це знати, щоб розробити ефективні в просторі структури даних. Однак я не можу уявити, чому ця інформація була б потрібна в коді програми під час виконання за допомогою виклику методу.
Поверхова відповідь полягає в тому, що Java не надає нічого подібного до розміру C (). Однак давайте розглянемо, чому програміст Java може іноді цього захотіти.
Програміст змінного струму сам керує більшістю розподілів пам'яті в структурі даних, і sizeof () є необхідним для знання розмірів блоків пам'яті, які слід розподілити. Крім того, розподільники пам'яті C, такі як malloc (), майже нічого не роблять щодо ініціалізації об'єкта: програміст повинен встановити всі поля об'єктів, які є вказівниками на подальші об'єкти. Але коли все сказано та закодовано, розподіл пам'яті C / C ++ є досить ефективним.
Для порівняння, виділення та побудова об'єктів Java пов'язані між собою (неможливо використовувати виділений, але неініціалізований екземпляр об'єкта). Якщо клас Java визначає поля, які є посиланнями на інші об'єкти, їх також часто встановлюють під час побудови. Розподіл об’єкта Java часто виділяє численні взаємопов’язані екземпляри об’єкта: графік об’єкта. У поєднанні з автоматичним збиранням сміття це занадто зручно і може викликати у вас відчуття, що вам ніколи не доведеться турбуватися про деталі виділення пам'яті Java.
Звичайно, це працює лише для простих програм Java. Порівняно з C / C ++, еквівалентні структури даних Java, як правило, займають більше фізичної пам'яті. При розробці корпоративного програмного забезпечення наближення до максимально доступної віртуальної пам'яті на сучасних 32-розрядних JVM є загальним обмеженням масштабованості. Таким чином, програміст Java може отримати вигоду від sizeof () або чогось подібного, щоб стежити за тим, чи його структури даних стають занадто великими або містять вузькі місця в пам'яті. На щастя, відображення Java дозволяє писати такий інструмент досить легко.
Перш ніж продовжувати, я відмовлюся від деяких частих, але неправильних відповідей на питання цієї статті. Помилка: Sizeof () не потрібен, оскільки розміри базових типів Java фіксовані
Так, Java int становить 32 біти у всіх JVM і на всіх платформах, але це лише вимога до специфікації мови для сприйнятої програмістом ширини цього типу даних. Такий int по суті є абстрактним типом даних і може бути підкріплений, скажімо, 64-бітовим словом фізичної пам'яті на 64-бітній машині. Те саме стосується і непримітивних типів: специфікація мови Java нічого не говорить про те, як поля класу повинні бути вирівняні у фізичній пам’яті, або про те, що масив логічних типів не може бути реалізований як компактний bitvector всередині JVM. Помилка: Ви можете виміряти розмір об’єкта, серіалізуючи його в байтовий потік і дивлячись на результуючу довжину потоку
Причина цього не працює, оскільки макет серіалізації - це лише віддалене відображення справжнього макета в пам'яті. Один простий спосіб побачити це, подивившись, як рядки серіалізуються: в пам’яті кожен символ складає щонайменше 2 байти, але в серіалізованій формі Рядки кодуються UTF-8, і тому будь-який вміст ASCII займає вдвічі менше місця
Java Native Access зазвичай використовується для виклику власних спільних бібліотек з Java. У цій бібліотеці існують методи визначення розміру об'єктів Java:
getNativeSize(Class cls)Метод і його перевантажені забезпечать розмір для більшості класів.
Як варіант, якщо ваші класи успадковуються від класу Структура JNA, calculateSize(boolean force)метод буде доступний.
Ви можете виконати бітові маніпуляції, як показано нижче, щоб отримати розмір примітивів:
public int sizeofInt() {
int i = 1, j = 0;
while (i != 0) {
i = (i<<1); j++;
}
return j;
}
public int sizeofChar() {
char i = 1, j = 0;
while (i != 0) {
i = (char) (i<<1); j++;
}
return j;
}
i<<8замість цього дасть кількість байтів.
Існує сучасний спосіб зробити це для примітивів. Використання BYTESтипів.
System.out.println("byte " + Byte.BYTES);
System.out.println("char " + Character.BYTES);
System.out.println("int " + Integer.BYTES);
System.out.println("long " + Long.BYTES);
System.out.println("short " + Short.BYTES);
System.out.println("double " + Double.BYTES);
System.out.println("float " + Float.BYTES);
Це призводить до,
byte 1
char 2
int 4
long 8
short 2
double 8
float 4
Клас Instrumentation має метод getObjectSize (), однак вам не потрібно використовувати його під час виконання. Найпростіший спосіб вивчити використання пам’яті - це використовувати профайлер, призначений для відстеження використання пам’яті.
EhCache надає клас SizeOf, який намагатиметься використовувати інструмент Instrumentation і повернеться до іншого підходу, якщо агент не завантажений або не може бути завантажений ( докладніше тут ).
Також дивіться агента від Хайнца Кабуца .
Спробуйте java.lang.Instrumentation.getObjectSize (Object) . Але будь ласка, майте це на увазі
Він повертає конкретне для реалізації наближення обсягу пам’яті, споживаного зазначеним об’єктом. Результат може включати деякі або всі накладні витрати на об'єкт, і, отже, корисний для порівняння в рамках реалізації, але не між реалізаціями. Оцінка може змінитися під час одного виклику JVM.
На SourceForge.net доступний клас / jar, який використовує інструменти Java для обчислення розміру будь-якого об’єкта. Ось посилання на опис: java.sizeOf
Тільки деякі тести щодо цього:
public class PrimitiveTypesV2 {
public static void main (String[] args) {
Class typesList[] = {
Boolean.class , Byte.class, Character.class, Short.class, Integer.class,
Long.class, Float.class, Double.class, Boolean.TYPE, Byte.TYPE, Character.TYPE,
Short.TYPE, Integer.TYPE, Long.TYPE, Float.TYPE, Double.TYPE
};
try {
for ( Class type : typesList ) {
if (type.isPrimitive()) {
System.out.println("Primitive type:\t" + type);
}
else {
boolean hasSize = false;
java.lang.reflect.Field fields[] = type.getFields();
for (int count=0; count<fields.length; count++) {
if (fields[count].getName().contains("SIZE")) hasSize = true;
}
if (hasSize) {
System.out.println("Bits size of type " + type + " :\t\t\t" + type.getField("SIZE").getInt(type) );
double value = type.getField("MIN_VALUE").getDouble(type);
long longVal = Math.round(value);
if ( (value - longVal) == 0) {
System.out.println("Min value for type " + type + " :\t\t" + longVal );
longVal = Math.round(type.getField("MAX_VALUE").getDouble(type));
System.out.println("Max value for type " + type + " :\t\t" + longVal );
}
else {
System.out.println("Min value for type " + type + " :\t\t" + value );
value = type.getField("MAX_VALUE").getDouble(type);
System.out.println("Max value for type " + type + " :\t\t" + value );
}
}
else {
System.out.println(type + "\t\t\t type without SIZE field.");
}
} // if not primitive
} // for typesList
} catch (Exception e) {e.printStackTrace();}
} // main
} // class PrimitiveTypes