Caros amigos!
Estou lendo um livro sobre Java (Use a cabeça) e no apendice B, pag.464, tem o seguinte tópico:
Usa o seguinte código como exemplo:
String s = "0"
for (int i=1;x<10;x++) {
s = s + x;
}
O autor descreve que:
O problema:
[quote]… outro problema do reservatório de Strings é que o Coletor de Lixo não chega até lá. Portando em nosso exemplo, a menos que por
coincidência posteriormente você crie uma String chamada digamos, “01234”, as primeiras nove Strings criadas em nosso loop for ficarão
aguardando apenas disperdiçando memória.[/quote]
Alternativa:
Quer dizer que não devo usar objetos do tipo String nos meus programas senão corro o risco de ficar sem memória?
Será que essas afirmações são válidas apenas para o escopo de existência da variável s?
Em que momento essas Strings são descartadas?
A quem puder esclarecer, antecipadamente agradeço.
1 - Não use o construtor de String, aproveite o pool.
2 - Se precisar de processamento intenso em Strings (concatenação) prefira o StringBuilder ao StringBuffer caso o objeto em questão não seja compartilhado entre Threads (o que na maioria das vezes não é). O primeiro não é sincronizado, sendo mais rápido.
3 - Compare Strings e qualquer outro objeto sempre usando o método equals. Deixe os operadores == e != apenas para primitivos, a não ser que você precise mesmo comparar a igualdade entre os endereços de objetos.
Não sei de onde o autor do livro tirou que o GC não chega ao reservatório de Strings. Essa afirmação só é verdadeira a VM da Sun, mas não é parte da especificação. A princípio, uma String não referenciada é sugeita ao gc.
Outra coisa, a única String no pool de Strings do seu exemplo é a primeira. Todas as demais, criadas a partir de concatenação, não são internalizadas, ou seja, inseridas no reservatório. E, portanto, mesmo na VM da Sun, são sujeitas a coleta de lixo.
Apenas complementando mais ainda: o operador + pode, sim, ser usado em concatenações porque o compilador faz a troca automaticamente por um StringBuilder, ou seja, este código:
String s = "Bem vindo, " + nomeUsuario + "!";
Será trocado pelo compilador por:
String s = new StringBuilder("Bem vindo, ").append(nomeUsuario).append("!").toString();
O problema é que nem sempre o compilador poderá fazer essa troca e, caso consiga, pode ser que ela não tenha efeito benéfico (como no caso do primeiro exemplo do efs.santos). Por isso é bom usarmos apenas em uma declaração de String. Algo como:
throw new IllegalArgumentException("O agumento " + argumentoDoMetodo + " é inválido");
Até porque isto seria horrível demais para se ler:
throw new IllegalArgumentException(new StringBuilder("O agumento ").append(argumentoDoMetodo).append(" é inválido"));
Lembrando que também podemos usar o método String.format para essas coisas.
Uai, pq então o NetBeans sugere o uso de um StringBuilder quando se concatena Strings no código? Ataxexe, você pode postar onde você viu isso? Se essa otimização existe, eu não sabia.
Se uma classe tiver seu classloader coletado, as suas strings estão disponíveis para limpeza
Se você usar String.intern, o pool de strings na verdade usa apenas uma referência fraca. Portanto, eventualmente podem ser limpas também. Tente rodar o seguinte programa:
import java.util.*;
class GCPoolString {
public static void printMemory (String title) {
Runtime r = Runtime.getRuntime();
System.out.println (title + ": " + "free = " + r.freeMemory() + ", total = " + r.totalMemory() + ", max = " + r.maxMemory());
}
public static void main(String[] args) {
System.gc();
try { Thread.sleep (3000); } catch (InterruptedException ex) {}
printMemory ("After GC 1");
System.out.println ("Now interning 1M strings in string pool");
// Internando um milhão de strings no string pool
for (int i = 1; i < 1000000; ++i) {
new String ("" + i).intern();
}
printMemory ("After interning 1M strings");
System.gc();
try { Thread.sleep (3000); } catch (InterruptedException ex) {}
printMemory ("After GC 2");
System.out.println ("Now interning 1M strings in string pool and keeping a reference to each string");
List<String> list = new ArrayList<String>();
for (int i = 1; i < 1000000; ++i) {
list.add (new String ("" + i).intern());
}
printMemory ("After interning 1M strings and keeping a reference to each one");
System.gc();
try { Thread.sleep (3000); } catch (InterruptedException ex) {}
printMemory ("After GC 3");
System.out.println ("Now removing the reference to the interned strings");
list.clear(); list = null;
try { Thread.sleep (3000); } catch (InterruptedException ex) {}
printMemory ("After removing the reference...");
System.gc();
try { Thread.sleep (3000); } catch (InterruptedException ex) {}
printMemory ("After GC 4");
}
}
Uai, pq então o NetBeans sugere o uso de um StringBuilder quando se concatena Strings no código? Ataxexe, você pode postar onde você viu isso? Se essa otimização existe, eu não sabia.
[]´s[/quote]
Eu dei uma olhada no bytecode gerado
Eu percebi isso uma vez quando apertei F5 no meio de uma expressão dessas no depurador e o Eclipse foi direto na classe StringBuilder. Aí eu resolvi dar uma olhada no que aconteceu e percebi que o compilador fazia a troca.