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?
HAHAHAHHA
ResponderExcluirIsso é tipo uma pegadinha?
O primeiro 'someData' nunca sai do escopo porque está dentro de um bloco em um contexto estático, logo ele nunca é coletado, pois 'teoricamente' está sendo usado. Afinal se trata de uma referência normal.
Quando se anula a referência a VM entende que não será mais utilizada.
Quase uma pegadinha =P.
ResponderExcluirMas o final do contexto faz sim com que a variavel saia de escopo e seja coletada - ou pelo menos deveria.
De qualquer modo, isso não explicaria o 3o caso - o que funciona com o System.gc() dentro de um for.
Verdade... o 3o exemplo funciona... parece que ele está se perdendo com o gc nos blocos, como se tivesse postergando a coleta... vou tentar dah uma procurada sobre como ele trata esse tipo de situação... bizarro! =)
ResponderExcluir