OutOfMemoryError - а где вся память?

Модератор: Absurd

Ответить
Аватара пользователя
Oscar
Сообщения: 963
Зарегистрирован: 29 май 2004, 13:44
Откуда: Мюнхен (рожден в Киеве)
Контактная информация:

Здравствуйте,
не могу понять такую вот ситуацию:

в нижеприведённом коде вылетает OutOfMemoryError, хотя консоль "говорит", что памяти должно хватать.

[syntax="Java"]public class Memory {

private static String console = "";
private static boolean running = true;

private static String formatMb(long bytes) {

float megaBytes = bytes;

megaBytes /= 1024;
megaBytes /= 1024;

int round = Math.round(megaBytes * 100);

megaBytes = round;

megaBytes /= 100;

return megaBytes + " Mb";
}

public static void main(String[] args) {

synchronized(console) {
console += "Total memory: \t" + formatMb(Runtime.getRuntime().totalMemory()) + "\n";
}

Thread m = new Thread() {

public void run() {
while(running) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized(console) {
console += "Memory used: \t" + formatMb(Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) + "\n";
}
}
}
};

m.start();

try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}

try {
int size = 20000*20000;

new java.awt.image.DataBufferInt(size);
} catch (OutOfMemoryError e) {
synchronized(console) {
console += e + "\n";
}
}

try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}

running = false;

synchronized(console) {
System.out.println(console);
}
}

}
[/syntax]

Программка запускается через:

Код: Выделить всё

java -Xms1G -Xmx1G Memory
И вывод на консоль идёт:
Total memory: 1016.13 Mb
Memory used: 2.52 Mb
Memory used: 2.52 Mb
...
Memory used: 20.45 Mb
Memory used: 21.73 Mb
Memory used: 21.73 Mb
Memory used: 21.73 Mb
Memory used: 21.73 Mb
Memory used: 21.73 Mb
Memory used: 21.73 Mb
Memory used: 21.73 Mb
Memory used: 21.73 Mb
Memory used: 21.73 Mb
Memory used: 21.73 Mb
Memory used: 21.73 Mb
Memory used: 21.73 Mb
Memory used: 21.73 Mb
Memory used: 21.73 Mb
Memory used: 21.73 Mb
Memory used: 21.73 Mb
Memory used: 23.05 Mb
Memory used: 23.05 Mb
Memory used: 23.05 Mb
Memory used: 23.05 Mb
Memory used: 23.05 Mb
Memory used: 23.05 Mb
Memory used: 23.05 Mb
Memory used: 23.05 Mb
Memory used: 23.05 Mb
Memory used: 23.05 Mb
Memory used: 23.05 Mb
Memory used: 23.05 Mb
Memory used: 23.05 Mb
Memory used: 23.05 Mb
Memory used: 23.05 Mb
Memory used: 24.33 Mb
Memory used: 24.33 Mb
java.lang.OutOfMemoryError: Java heap space
Memory used: 2.69 Mb
Memory used: 2.69 Mb
Memory used: 2.69 Mb
...
Вывод используемой памяти на консоль происходит довольно часто и прирост 1-2 Мб максимум, т.о. перед тем, как вылетел Exception используемая память была 24 Мб.
Какая бы ни была погрешность, это на порядок меньше, чем доступная память.

И вопрос, который меня в связи с этим волнует, звучит так: Каким образом может вылетать OutOfMemoryError, если памяти достаточно?
Быть может я не понимаю сути этого Эксепшна, тогда обьясните, плз, кто знает.

В общем я в полной растеряности ...
mobius
Сообщения: 157
Зарегистрирован: 25 янв 2005, 18:42
Откуда: Минск
Контактная информация:

Попробуй вместо String console использовать Ststem.out, по крайней мере не будет память кушать этой строкой (возможно фактически у тебя получается ну очень большая строка):

Код: Выделить всё

console += "Memory used: \t" + formatMb(Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) + "\n";
Так же целесообразно отображать оставшуюся свободную пямять
Всё об отдыхе на http://chugaga.com
Аватара пользователя
Oscar
Сообщения: 963
Зарегистрирован: 29 май 2004, 13:44
Откуда: Мюнхен (рожден в Киеве)
Контактная информация:

&quot писал(а):Попробуй вместо String console использовать Ststem.out,
Отнють, эту конструкцию я использовал специально, чтобы вывод на консоль происходил, как бы, realtime. В противном случае сообщение об ошибке смещается на пару сообщений вниз (что не критично, но всё же).
Кроме того, (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) возвращает ВСЮ использованую память (включая и затраты на String_и), так что на результат проверки наличия памяти это никак не влияет.
&quot писал(а):Так же целесообразно отображать оставшуюся свободную пямять
Спасибо за советы, конечно, но это не является ответом на поставленый вопрос.
Аватара пользователя
Oscar
Сообщения: 963
Зарегистрирован: 29 май 2004, 13:44
Откуда: Мюнхен (рожден в Киеве)
Контактная информация:

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

Легче от этого не стало, но расскажу, может ещё кто будет голову ломать над подобным вопросом.

Внутри DataBufferInt лежит не что иное, как массив типа int, который алокируется, как :

int[] data = new int[size];

и Ява, чтобы не напрягать компьютер, просто проверяет, влезет ли массив такого размера в память, или нет. Если не влезает - берёт и бросает OutOfMemoryError.

К такому выводу я пришел после упрощения кода до:

[syntax="Java"]public class Memory {

public static void main(String[] args) {

try {
int temp = 20000;

int[] data = new int[temp * temp];
} catch (OutOfMemoryError e) {
e.printStackTrace();
}

}

}[/syntax]

размер хипа всё тот же 1 Гб.

и вот если temp = 20000 программа вылетает моментально, а при temp = 15000 комп долго тужится и успешно завершает работу программы.

P.S. а то, что Memory used увеличивалось, а потом прыгало на "ноль" после прохода GC - так это и были те temp_овые String_и, которые я добавлял к переменной console.
Ответить