2014-11-11 2 views
1

Я пытаюсь выяснить, сколько памяти используется массивом внутри JVM. Я настроил программу для этой цели, что дает мне нечетные результаты.Сколько памяти использует массив Java?

protected static long openMem(){ 
    System.gc(); 
    System.runFinalization(); 
    return Runtime.getRuntime().freeMemory(); 
} 

public static double listSize(int limit){ 
    long start= openMem(); 
    Object[] o= new Object[limit]; 
    for(int i= 0; i<limit; i++){ 
     o[i]= null; 
    } 
    long end= openMem(); 
    o= null; 
    return (start-end); 
} 

public static void list(int i){ 
    for(int y= 0; y<50; y++){ 
     double d= Quantify.listSize(i); 
     System.out.println(i+" = "+d+" bytes"); 
    } 
} 

public static void main(String ... args){ 
     list(1); 
     list(2); 
     list(3); 
     list(100); 
    } 

Когда я запускаю это, я получаю два разных байт-размера для каждого размера массива, например:

  • 1 = 24,0 байт
  • 1 = 208,0 байт
  • 1 = 24,0 байт
  • 1 = 208,0 байт
  • 1 = 208,0 байт
  • 1 = 208,0 байт
  • 1 = 208,0 байт
  • 1 = 24,0 байт

Так массив из 1 элемента только когда-либо возвращает «24 байта» или «208 байт», и та же картина имеет место для всех остальных:

1 = 24,0 байт

1 = 208,0 байт

2 = 24,0 байт

2 = 208,0 байт

3 = 32,0 байт

3 = 216,0 байт

100 = 416,0 байт

100 = 600,0 байт

Я пытаюсь выяснить, почему это является. Мне интересно, может ли кто-нибудь еще здесь (а) уже знать ответ, или (б) знает, как найти ответ.

+1

Я никогда не рекомендую измерять что-либо через дедушки freemem.Создайте некоторый типизированный массив, например MyClass [], и запустите jmap -histo: live . Память не является детерминированной во всяком случае, и Java runtime может иметь свои собственные кеши и объекты, потоки, поэтому вы можете количественно определить что-либо через delta freemem, вы можете получить номер шара для текущей используемой или свободной памяти, но пытаться измерить один объект (как массив) - бесполезное усилие. Массив потребляет заголовок объекта + 4bytes length + sun.misc.Unsafe.ADDRESS_SIZE * array.length – bestsss

ответ

2

Измерение занятости кучи на JVM даже сложнее, чем измерение производительности. Во-первых, есть поточно-локальные буферы распределения (TLAB), которые являются кусками кучи, выделенными одновременно, независимо от того, какой размер объекта выделяется. Вы должны отключить их использование для измерения: -XX:-UseTLAB. Кроме того, ваш код делает некоторые вещи правильно, но другие практически правы. Например, я предложил бы запустить два GC; нет необходимости запускать финализацию; и запустить GC перед распределением, затем после освобождения. Вы запускаете его только перед каждым измерением. Вам также необходимо использовать totalMemory-freeMemory, в противном случае вы уязвимы для изменения размера кучи.

В целом, попробуйте измерить с помощью этого кода, это дает мне надежные результаты.

class Quantify { 
    static final Object[][] arrays = new Object[20][]; 

    static long takenMem(){ 
    final Runtime rt = Runtime.getRuntime(); 
    return rt.totalMemory() - rt.freeMemory(); 
    } 

    static long arraySize(int size){ 
    System.gc(); System.gc(); 
    long start = takenMem(); 
    for (int i = 0; i < arrays.length; i++) arrays[i] = new Object[size]; 
    final long end = takenMem(); 
    for (int i = 0; i < arrays.length; i++) arrays[i] = null; 
    System.gc(); System.gc(); 
    return (end - start)/arrays.length; 
    } 
    public static void main(String... args) { 
    for (int i = 1; i <= 20; i++) System.out.println(i+": "+arraySize(i)); 
    } 
} 

я получаю этот выход:

1: 24 
2: 24 
3: 32 
4: 32 
5: 40 
6: 40 
7: 48 
8: 48 
9: 56 
10: 56 
11: 64 
12: 64 
13: 72 
14: 72 
15: 80 
16: 80 
17: 88 
18: 88 
19: 96 
20: 96 

Это согласуется с реальной ситуацией: минимальное выделение составляет 24 байт из-за накладные расходы заголовков; размеры изменяются на 8 из-за проблем с выравниванием памяти (это типично для 64-битной JVM).

+0

гораздо более простой способ измерения: 'jmap -histo' – bestsss

+1

Я сжег один раз с такими метриками с помощью VisualVM (он, вероятно, полагается на' jmap' или аналогично внутренне): он не учитывал сжатые ООП. Это указывает на то, что его отчеты не основаны на прямом измерении, а на некоторых жестко закодированных данных 'sizeof', которые могут быть несовместимы с реальностью. –

+0

jmap и jstack используют интерфейс отладки для подключения к jvm, visualvm, вероятно, использует тот же интерфейс (не большой поклонник VisualVM, поскольку он не может быть действительно использован в производстве). Однако все моментальные снимки выполняются в хост-процессе, поэтому, если CompressedOptions не учитывался, вероятно, это была некоторая ошибка. В целом я очень рекомендую «мастеринг» jmap, поскольку он был незаменимым инструментом для профилирования производственных серверов для распределения/использования памяти и утечек. – bestsss

Смежные вопросы