OutOfMemoryError (??)
Autor: takeshi | Publicado: 2010-09-24T14:58:00-03:00

Embora o título desse post possa parecer estranho, ele refere-se a um OutOfMemoryError que pode ocorrer em uma situação (bem específica é verdade), mas que aparentemente não deveria. Sem mais delongas, segue o código: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?