Oi! Acho que você não ficaria muito surpreso se eu dissesse que seu computador tem uma quantidade limitada de memória :)
Até mesmo seu disco rígido (que é muitas, muitas vezes o tamanho da RAM) pode ficar cheio de seus jogos favoritos, programas de TV e outras coisas. Para evitar que isso aconteça, você precisa monitorar o estado atual da memória do seu computador e excluir arquivos desnecessários. Como tudo isso se relaciona com a programação Java? Muito diretamente! Afinal, criar qualquer objeto faz com que a máquina Java aloque memória para ele . Um grande programa do mundo real cria dezenas ou centenas de milhares de objetos, e um pedaço de memória é alocado para cada um deles. Mas o que você acha, quantos desses objetos existem? Eles estão "vivos" o tempo todo enquanto nosso programa está em execução? Claro que não. Mesmo com todas as suas vantagens, os objetos Java não são imortais :) Os objetos têm seu próprio ciclo de vida. Hoje vamos fazer uma pequena pausa na escrita de código e explorar esse processo :) Também é muito importante para entender como um programa funciona e para gerenciar recursos. Então, onde começa a vida de um objeto? Como um ser humano, desde o nascimento, ou seja, quando é criado.
O coletor de lixo é um mecanismo interno do Java responsável por liberar memória, ou seja, remover objetos desnecessários da memória. Há uma razão pela qual escolhemos representá-lo com um aspirador de pó robô. O coletor de lixo funciona mais ou menos da mesma maneira: ele "move" seu programa em segundo plano, coletando lixo. Você praticamente não precisa interagir com ele. Sua função é deletar objetos que não são mais usados no programa. Assim, libera memória para outros objetos. Você se lembra que no início da aula dissemos que na vida real você deve monitorar o estado do seu computador e deletar arquivos antigos? Se estamos falando de objetos Java, o coletor de lixo faz isso para você. O coletor de lixo é iniciado várias vezes enquanto seu programa é executado: você não precisa chamá-lo explicitamente e dar comandos (embora isso seja tecnicamente possível). Falaremos mais sobre o coletor de lixo mais adiante e analisaremos com mais detalhes como ele funciona. Quando o coletor de lixo atinge um objeto - pouco antes de ser destruído - o
Para que o aspirador funcione bem, é preciso manter o chão em bom estado e recolher tudo o que ele não aguenta. O coletor de lixo segue o mesmo princípio. Se um programa tiver muitos objetos que não pode limpar (como uma meia ou Lego para nosso aspirador de pó robótico), um dia ficaremos sem memória. Não apenas o seu programa travará, mas também todos os outros programas que estiverem em execução no computador. Afinal, eles também não terão memória suficiente (voltando à nossa analogia, o vidro quebrado no chão para não só o aspirador, mas também as pessoas que moram na casa). Resumindo, é assim que o ciclo de vida do objeto e a coleta de lixo se parecem em Java. Você não precisa memorizar isso: basta simplesmente entender como funciona. Na próxima lição, nós Voltarei a esses processos com mais detalhes. Mas, por enquanto, você pode voltar a resolver as tarefas do CodeGym :) Boa sorte!
Cat cat = new Cat();// Our Cat object's lifecycle begins now!
Primeiro, a máquina virtual Java aloca a memória necessária para criar o objeto. Em seguida, ele cria uma referência a ele (no nosso caso, cat
) para possibilitar o acompanhamento. Em seguida, todas as variáveis são inicializadas, o construtor é chamado e nosso novo objeto agora está vivendo sua própria vida :) O tempo de vida do objeto varia. Não há números exatos aqui. De qualquer forma, um objeto vive no programa e desempenha suas funções por algum período de tempo. Para ser preciso, o objeto está "vivo" enquanto houver referências a ele. Assim que não houver referências, o objeto "morre". Por exemplo:
public class Car {
String model;
public Car(String model) {
this.model = model;
}
public static void main(String[] args) {
Car lamborghini = new Car("Lamborghini Diablo");
lamborghini = null;
}
}
No main()
método, o objeto Car "Lamborghini Diablo" deixa de estar vivo na segunda linha. Havia apenas uma referência a ele e a referência foi definida como nula. Como não há referências restantes ao Diablo, ele se torna "lixo". Uma referência não precisa ser definida como zero para que isso aconteça:
public class Car {
String model;
public Car(String model) {
this.model = model;
}
public static void main(String[] args) {
Car lamborghini = new Car("Lamborghini Diablo");
Car lamborghiniGallardo = new Car("Lamborghini Gallardo");
lamborghini = lamborghiniGallardo;
}
}
Aqui criamos um segundo objeto e o atribuímos à referência lamborghini. Agora, duas referências apontam para o Lamborghini Gallardo
objeto, mas o Lamborghini Diablo
objeto não tem nenhuma. Isso significa que o Diablo
objeto se torna lixo. É quando o coletor de lixo (GC) integrado do Java entra em ação.
finalize()
método especial do objeto é chamado. Este método pode ser invocado para liberar certos recursos adicionais usados pelo objeto. O finalize()
método pertence à classe Object. Em outras palavras, é semelhante a equals()
, hashCode()
e toString()
(que você conheceu anteriormente). Todo objeto tem isso . Ele difere de outros métodos porque... como devemos dizer isso... é muito obstinado. Com isso queremos dizer quenem sempre é chamado antes de um objeto ser destruído . A programação é uma atividade muito precisa. O programador diz ao computador para fazer algo, e o computador faz. Presumo que você esteja acostumado a esse tipo de comportamento, então, a princípio, pode ser difícil para você aceitar a seguinte ideia: "Antes de um objeto ser destruído, o método da classe Object é chamado. Ou não. finalize()
Se tivermos sorte! " Ainda assim, esta é a realidade. A própria máquina Java determina se deve chamar finalize() caso a caso. Como experiência, vamos tentar executar o seguinte código:
public class Cat {
private String name;
public Cat(String name) {
this.name = name;
}
public Cat() {
}
public static void main(String[] args) throws Throwable {
for (int i = 0 ; i < 1000000; i++) {
Cat cat = new Cat();
cat = null;// The first object becomes available for garbage collection here
}
}
@Override
protected void finalize() throws Throwable {
System.out.println("The Cat is destroyed!");
}
}
Criamos um Cat
objeto e na próxima linha zeramos a única referência a ele. E fazemos isso um milhão de vezes. Substituímos explicitamente o finalize()
método. Cada vez que um Cat
objeto é destruído, ele deve exibir uma string – um total de um milhão de vezes. Mas não! Para ser exato, no meu computador foi executado apenas 37346 vezes! Em outras palavras, minha máquina Java decidiu chamar o finalize()
método em apenas 1 de cada 27 casos. Nos demais casos, a coleta de lixo não envolvia essa chamada. Tente executar este código você mesmo. Você provavelmente obterá um resultado diferente. Como você pode ver, é difícil chamar finalize()
um parceiro confiável :) Então, aqui vai uma pequena dica para o futuro: não confie no finalize()
método para liberar recursos críticos.A JVM pode chamá-lo ou não. Quem sabe? Se seu objeto manteve alguns recursos críticos de desempenho (por exemplo, uma conexão de banco de dados aberta) enquanto estava ativo, seria melhor criar e chamar explicitamente um método especial para liberá-los quando o objeto não for mais necessário. Dessa forma, você terá certeza de que o desempenho do seu programa não será prejudicado. Começamos dizendo que trabalhar com memória e coleta de lixo são tópicos muito importantes, e de fato são. O manuseio incorreto de recursos e a má compreensão de como os objetos desnecessários são limpos podem levar a um dos erros mais desagradáveis: vazamentos de memória . Este é um dos erros de programação mais conhecidos. Ele ainda tem seu próprio artigo da Wikipedia. Código mal escrito pode criar uma situação em que a memória é sempre alocada para objetos recém-criados, mas objetos antigos e desnecessários ficam indisponíveis para coleta de lixo. Como já fizemos a analogia do robô aspirador de pó, imagine o que aconteceria se antes de executar o robô você espalhasse meias pela casa toda, quebrasse um vaso de vidro e deixasse peças de Lego espalhadas pelo chão. Naturalmente, o robô tentaria fazer alguma coisa, mas um dia travaria.
GO TO FULL VERSION