2010-09-23 3 views
3

Эй я тестируя Runtime.freeMemory() с этим кодом:Проблема с Runtime.freeMemory() - Java

Runtime runtime = Runtime.getRuntime(); 
long freeMemory = runtime.freeMemory(); 

// This is some arbitary factor that should work <= 1 
double factor = 0.8; 

int size = (int) (factor * freeMemory); 
byte[] testArray = new byte[size]; 

Я создаю массив байтов размера близко к FreeMemory значения. По какой-то причине, особенно когда я ограничиваю память программы примерно до 8 МБ, код генерирует исключение OutOfMemory для любого коэффициента> 0,55. Это действительно не имеет смысла, конечно, freeMemory означает freeMemory, я ожидаю, что это будет немного, но не вдвое больше, чем на самом деле бесплатно.

Любые предложения о том, что происходит? Благодаря

(Примечание в моих тестах IM ограничивая объем памяти, доступной для программы по 8Мб или 16Мб, используя -Xmx8M и т.д.)

+0

Из любопытства, каковы шансы, что вы переполняете 'int', когда вы бросаете и умножаете? –

+0

Хотелось бы, чтобы все было так просто. –

+0

Одной из причин, почему это может быть неудачно для больших значений 'factor', является то, что вы запрашиваете смежные байты в массиве. Возможно, вы сможете выделить более полную память, используя меньшие куски. – msandiford

ответ

0

Try мусора сбор перед созданием тестового массива с runtime.gc().

Если вы не создаете совершенно новый JVM каждый раз, когда можете получить бит, имея разные начальные состояния.


Шахта работает для значений> 1. 1.25, например. Тогда я получаю исключение «кучного пространства».


Здесь, возможно, вы хотите вместо этого «maxMemory()».

public class Mem2 { 
    public static void main(String[] args) { 


     Runtime runtime = Runtime.getRuntime(); 
     runtime.gc(); 

     long freeMemory = runtime.freeMemory(); 

     // This is some arbitary factor that should work <= 1 
     double factor = 1.29; 

     int size = (int) (factor * freeMemory); 
     System.out.println("  freememory is " + freeMemory); 
     System.out.println("   size is " + size); 
     System.out.println("the total memory is " + runtime.totalMemory()); 
     System.out.println(" the max memory is " + runtime.maxMemory()); 
     byte[] testArray = new byte[size]; 
    } 
} 

Выход:

 freememory is 84466864 
      size is 108962254 
the total memory is 85000192 
    the max memory is 129957888 

Process finished with exit code 0 

Так что, кажется, о 20М я не могу объяснить.

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


Вы можете увидеть взаимосвязь между totalMemory() и freememory() с этим вариантом на код.

public class Mem3 { 

    public static void main(String[] args) { 


     Runtime runtime = Runtime.getRuntime(); 

     for (int i = 0; true; i++) { 
      runtime.gc(); 
      int size = i * 10000000; 
      System.out.println("     i is " + i); 
      System.out.println("    size is " + size); 
      System.out.println("b  freememory is " + runtime.freeMemory()); 
      System.out.println("b the total memory is " + runtime.totalMemory()); 
      System.out.println("b the max memory is " + runtime.maxMemory()); 
      byte[] testArray = new byte[size]; 
      System.out.println("    array " + testArray.length); 
      System.out.println("a  freememory is " + runtime.freeMemory()); 
      System.out.println("a the total memory is " + runtime.totalMemory()); 
      System.out.println("a the max memory is " + runtime.maxMemory()); 
      System.out.println(" "); 
     } 

    } 
} 

Если вы запустите это и посмотрите на значения до и после, вы увидите, что происходит. Обратите внимание, что происходит между interations 6 и 7:

    i is 6 
       size is 60000000 
b  freememory is 84300496 
b the total memory is 85000192 
b the max memory is 129957888 
       array 60000000 
a  freememory is 24300472 
a the total memory is 85000192 
a the max memory is 129957888 

       i is 7 
       size is 70000000 
b  freememory is 84300496 
b the total memory is 85000192 
b the max memory is 129957888 
       array 70000000 
a  freememory is 59258168 
a the total memory is 129957888 
a the max memory is 129957888 

Мы видим в 6, что после выделения 60M, есть около 24М осталось. Однако в 7 мы превысили порог. Было выделено больше памяти (примечание totalMemory), а freeMemory сейчас чуть меньше 60M.

+0

Я также получаю коэффициент около 1.3, когда я тестирую его нормально, при выделенной памяти по умолчанию составляет 128 М, у меня возникают проблемы, когда я тестирую его примерно в 8 МБ или 16 МБ, используя -Xmx8M и т. Д. Кажется довольно случайным, на низкие уровни памяти, которые он не позволит мне сделать массив размером с свободную память, но на более высоких уровнях памяти вы можете переходить через него. –

+0

Причина, по которой вы можете перейти через свободную память, заключается в том, что ваше распределение будет инициировать сборку мусора, существенно увеличит объем доступной памяти. Это было бы особенно примечательно с большей памятью, поскольку ГК оставила больше мусора, лежащего дольше. – CurtainDog

2

На самом деле, ваша свободная память делится на два поколения: http://java.sun.com/docs/hotspot/gc1.4.2/ one - это «молодое» поколение, а одно - поколение. Если вы запускаете с параметром -verbose: gc -XX: + PrintGCDetails VM, вы можете видеть, сколько занимает каждое поколение. Я узнал, что могу полностью заполнить свое поколение, но не более того.

+0

Правильно: модель памяти сложна, и точное поведение будет зависеть от настройки JVM. – CurtainDog

0

Лучшим способом получить доступ к этому будет запуск кода через профилировщик, который сможет показать вам, как память в вашей системе распределяется с течением времени. Если вы используете Eclipse, убедитесь, что у вас установлен TPTP, я уверен, что другие большие IDE имеют одну и ту же функцию.

0

Я думаю, что это связано с размерами разделов кучи. Когда JVM запускается, он собирается разделить доступную память кучи на несколько «пробелов»; например есть пространство для вновь созданных объектов, пространство для объектов с сохранением целостности, пространство «eden» для объектов, которые выходят за первый цикл GC, и так далее. Фактические размеры пространств настраиваются (с помощью опций JVM), но есть вероятность, что пространство «новых объектов» значительно меньше 8 МБ.

Когда вы попытаетесь выделить свой массив, содержащий 55% свободной памяти, диспетчер памяти должен найти это количество смежной памяти в пространстве «новые объекты». Если вы получаете OOME, это связано с тем, что фактическое разделение таково, что требуемое количество смежных памяти не доступно ... даже после того, как GC запущен.

В принципе, вы пытаетесь запустить JVM со слишком маленькой кучей для того, что вы пытаетесь сделать. Как общий принцип, это плохая идея быть скупой с размером кучи Java. Ваше приложение будет работать быстрее (и с меньшими проблемами), если вы придадите ему много памяти.

0

Этот ответ основан на комментариях spong в главном quesiton.

Если вместо того, чтобы создавать массив в одном патроне, вы разбиваете его создание на множество меньших массивов, вы можете заполнить свободную память до коэффициента около 0,98.

 Runtime runtime = Runtime.getRuntime(); 
    long freeMemory = runtime.freeMemory(); 

    // This is some arbitary factor that should work <= 1 
    double factor = 0.8; 

    int size = (int) (factor * freeMemory); 


    int slice = 100; 
    byte[][] testArrays = new byte[slice][1]; 
    for (int i = 1; i <= slice; i++) { 
      testArrays[i-1] = new byte[size/slice]; 
      System.out.println("Allocated: " + i * size/slice); 
    } 

    System.out.println("Created! "+testArrays.length); 
Смежные вопросы