public class Foo {
public static void exec(int size) {
{
byte[] someData = new byte[size];
process(someData);
}
//final do bloco
byte[] someData = new byte[size];
process(someData);
System.out.println("ok");
}
private static void process(byte[] data) {
}
public static void main(String[] args) {
exec((int) (0.7 * Runtime.getRuntime().maxMemory()));
}
}
O código acima lança uma OutOfMemoryError: java heap space, muito embora não devesse, já que o objeto 'someData' sai de escopo após a linha 7, quando o bloco termina. A primeira impressão que pode ficar é, como as duas alocações estão próximas, não dá tempo para o GC limpar a memória. Tentaremos, então, chamar explicitamente o GC para ser executado no meio tempo:
public class Foo {
public static void exec(int size) {
{
byte[] someData = new byte[size];
process(someData);
}
//final do bloco
System.gc();
byte[] someData = new byte[size];
process(someData);
System.out.println("ok");
}
private static void process(byte[] data) {
}
public static void main(String[] args) {
exec((int) (0.7 * Runtime.getRuntime().maxMemory()));
}
}
Mesmo desse jeito, o código continua com o OutOfMemoryError. Algumas pessoas familiarizadas com o funcionamento do GC podem afirmar que chamar System.gc() não necessariamente implica no GC ser executado de verdade. Mas o código abaixo mostra(?) que, se formos insistentes, a memória é coletada apropriadamente:
public class Foo {
public static void exec(int size) {
{
byte[] someData = new byte[size];
process(someData);
}
//final do bloco
for(int i = 0; i < 10; i++) System.gc();
byte[] someData = new byte[size];
process(someData);
System.out.println("ok");
}
private static void process(byte[] data) {
}
public static void main(String[] args) {
exec((int) (0.7 * Runtime.getRuntime().maxMemory()));
}
}
E também podemos ver que gerenciando "manualmente" a memória (ou seja, anulando a variável someData ao final do bloco), o código funciona normalmente:
public class Foo {
public static void exec(int size) {
{
byte[] someData = new byte[size];
process(someData);
someData = null;
}
//final do bloco
byte[] someData = new byte[size];
process(someData);
System.out.println("ok");
}
private static void process(byte[] data) {
}
public static void main(String[] args) {
exec((int) (0.7 * Runtime.getRuntime().maxMemory()));
}
}
Por que será que se insistirmos, o problema some? Mais do que isso, há um problema com o gerenciamento de memória da VM? Devemos esquecer que o GC existe e gerenciar a coleta de objetos manualmente, anulando todas as variáveis quando elas deixam de ser usadas?