String imutável ? [RESOLVIDO]

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.

oi,

abra o código (fonte) da classe java.lang.String e irá entender como as coisas funcionam

existe uma coisa chamada pool de strings no java (leia isso depois)

você nunca deve fazer isso

String minhaString = new String("teste");

mas sim isso

String minhaString = "teste";

desta forma estará usando o pool

outra coisa, quando precisar fazer concatenação de strings prefira usar as classes StringBuffer ou StringBuilder ao invés de usar o operador ‘+’

por exemplo

[code]StringBuilder builderStr = new StringBuilder();
builderStr.append(“testando”);
builderStr.append(“testando”);
builderStr.append(“testando”);
builderStr.append(“testando”);
builderStr.append(“testando”);

String str = builderStr.toString();[/code]

ao invés de

String str = "testando" + "testando" + "testando";

Complementando a resposta do André:

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.

[]´s

Esclarecedor, obrigado!

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.

[]´s

Em ciclos deves usar StringBuilder porque o compilador não faz a optimização.

Nem lá na JVM da Sun essa afirmação é verdadeira.

  1. Se uma classe tiver seu classloader coletado, as suas strings estão disponíveis para limpeza
  2. 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 :slight_smile:

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.

[]'s

Só mais alguns pontos: uma expressão “abc” + “xyz” será trocada pelo compilador por “abcxyz” (somente se estiverem no início da concatenação).

Exemplo:

String s = "abc" + "xyz" + x;

Será trocado por:

String s = new StringBuilder("abcxyz").append(String.valueOf(x)).toString();

Porém, este código:

String s = x + "abc" + "xyz";

Será trocado por:

String s = new StringBuilder(String.valueOf(x)).append("abc").append("xyz").toString();

Para os curiosos, sugiro instalarem o plugin Bytecode Outline.