Coletor de lixo no Android

108

Tenho visto muitas respostas do Android que sugerem chamar o coletor de lixo em algumas situações.

É uma boa prática solicitar o coletor de lixo no Android antes de fazer uma operação que consome muita memória? Caso contrário, devo chamá-lo apenas se receber um OutOfMemoryerro?

Há outras coisas que devo usar antes de recorrer ao coletor de lixo?

hpique
fonte

Respostas:

144

Para versões anteriores a 3.0 honeycomb : Sim, ligue System.gc() .

Tentei criar bitmaps, mas sempre obtinha "erro de VM sem memória". Mas, quando liguei System.gc()primeiro, estava tudo bem.

Ao criar bitmaps, o Android geralmente falha com erros de falta de memória e não tenta coletar o lixo primeiro . Portanto, chame System.gc()e você terá memória suficiente para criar Bitmaps.

Se estiver criando objetos, acho System.gcque será chamado automaticamente se necessário, mas não para criar bitmaps. Simplesmente falha.

Portanto, eu recomendo chamar manualmente System.gc()antes de criar bitmaps.

Steve
fonte
39
Isso ocorre porque os dados de bitmap pré-honeycomb não são armazenados na VM e, portanto, não acionam o GC. Não deve ser um problema no Honeycomb e posteriores.
Timmmm
2
@Timmmm mas na verdade o problema era meu, acabei de definir o grande heap como verdadeiro.
Lei Leyba de
118

De modo geral, na presença de um coletor de lixo, nunca é uma boa prática chamar manualmente o GC. Um GC é organizado em torno de algoritmos heurísticos que funcionam melhor quando deixados por conta própria. Chamar o GC manualmente geralmente diminui o desempenho.

Ocasionalmente , em algumas situações relativamente raras, pode-se descobrir que um determinado GC está errado e uma chamada manual para o GC pode melhorar as coisas em termos de desempenho. Isso ocorre porque não é realmente possível implementar um GC "perfeito" que gerenciará a memória de forma otimizada em todos os casos. Essas situações são difíceis de prever e dependem de muitos detalhes sutis de implementação. A "boa prática" é deixar o GC funcionar sozinho; uma chamada manual para o CG é a exceção, que deve ser prevista somente após um problema de desempenho real ter sido devidamente testemunhado.

Thomas Pornin
fonte
8
1 para uma boa resposta independente de plataforma. Esperando para ver se alguém vem com uma resposta específica do Android antes de escolher.
hpique
8
Concordo com o que você escreveu se permitir que "desempenho" signifique algo mais geral do que a eficiência da CPU. Em um dispositivo Android, a percepção de desempenho do usuário é fundamental. O desenvolvedor pode preferir executar o GC enquanto o usuário pressiona os botões, por exemplo, para que o usuário não esteja ciente do GC.
Presidente James K. Polk
5
Geralmente, é importante em jogos que o GC não seja executado enquanto o jogo está em execução (isso requer que todos os objetos sejam pré-criados e reciclados ou semelhantes), mas quando o jogo é pausado, é um bom momento para GC.
Pat
4
Pré-honeycomb, os dados de bitmap não são armazenados na memória VM. O sistema não detectará quando você usou muita memória não-VM devido a bitmaps e executou o GC (que executaria os Bitmap.finalize()métodos que liberam a memória não-VM). Portanto, neste caso, você deve passar por cima da cabeça do GC e executar Bitmap.recycle()ou System.gc()onde for apropriado. Mas apenas pré-favo de mel.
Timmmm
1
@ThomasPornin - Por outro lado, como um programador de aplicativos, você sabe algo que o sistema operacional não sabe: momentos em que seu aplicativo agora fará uma grande mudança na forma como usa a memória e que seria menos prejudicial para a experiência do usuário para fazer uma pausa agora, do que em um momento arbitrário no futuro . Isso significa que pode ser sensato fazer chamadas pouco frequentes , em grandes transições na forma como o aplicativo está sendo usado. Eles são raros no sentido de que não se deve chamar GC com frequência, mas são comuns em que quase todos os aplicativos significativos têm esses momentos conhecidos por design.
ToolmakerSteve
26

Falta de memória no aplicativo Android é muito comum se não manusearmos o bitmap adequadamente. A solução para o problema seria

if(imageBitmap != null) {
    imageBitmap.recycle();
    imageBitmap = null;
}
System.gc();
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 3;
imageBitmap = BitmapFactory.decodeFile(URI, options);
Bitmap  scaledBitmap = Bitmap.createScaledBitmap(imageBitmap, 200, 200, true);
imageView.setImageBitmap(scaledBitmap);

No código acima, tentei reciclar o bitmap, o que permitirá que você libere o espaço de memória usado, portanto, a falta de memória pode não acontecer. Tentei funcionar para mim.

Se ainda estiver enfrentando o problema, você também pode adicionar essas linhas

BitmapFactory.Options options = new BitmapFactory.Options();
options.inTempStorage = new byte[16*1024];
options.inPurgeable = true;

para mais informações, dê uma olhada neste link

https://web.archive.org/web/20140514092802/http://voices.yahoo.com/android-virtual-machine-vm-out-memory-error-7342266.html?cat=59


NOTA: Devido ao momentânea "pausa" causada pela realização de gc, é que não é recomendado para fazer isso antes de cada alocação de bitmap.

O design ideal é:

  1. Libertar todos os bitmaps que já não são necessários , pelo if / recycle / nullcódigo mostrado. (Faça um método para ajudar nisso.)

  2. System.gc();

  3. Aloque os novos bitmaps.

yogesh
fonte
19

Se você obtiver um OutOfMemoryError, geralmente é tarde demais para chamar o coletor de lixo ...

Aqui está uma citação do desenvolvedor Android:

Na maioria das vezes, a coleta de lixo ocorre por causa de toneladas de objetos pequenos e de curta duração e alguns coletores de lixo, como coletores de lixo geracionais, podem otimizar a coleta desses objetos para que o aplicativo não seja interrompido com muita frequência. O coletor de lixo do Android infelizmente não é capaz de realizar tais otimizações e a criação de objetos de curta duração em caminhos de código de desempenho crítico é, portanto, muito cara para seu aplicativo.

Portanto, no meu entendimento, não há necessidade urgente de ligar para o gc. É melhor despender mais esforço para evitar a criação desnecessária de objetos (como a criação de objetos dentro de loops)

Andreas Dolk
fonte
6
A citação não pode ser interpretada de outra maneira? Talvez o GC precise ser chamado manualmente para coletar esses objetos antes que eles consumam muita memória.
hpique
@hgpc: Não vejo como a citação pode ser interpretada da maneira que você sugere. Estou supondo que a documentação é uma confissão, admitindo que seu GC é simples; quando a memória fica baixa, um GC completo é executado.
Presidente James K. Polk
Fazer uso total de objetos complexos e estruturas que usam objetos complexos tende a usar melhor o tempo de desenvolvimento do que a otimização prematura. A preocupação com a otimização é uma armadilha mais fácil para o Android do que para o Enterprise Java, pois subconscientemente continuamos pensando sobre o desempenho ao codificar um aplicativo móvel. Especialmente porque os desenvolvedores do framework, que precisam pensar no desempenho, vazam esse ponto de vista em sua documentação oficial quando não são cuidadosos.
LateralFractal
9

Parece System.gc()não funcionar no Art Android 6.0.1 Nexus 5x, então eu uso em seu Runtime.getRuntime().gc();lugar.

cmicat
fonte
1
System.gc()é uma função de wrapper para Runtime.getRuntime().gc(). Consulte android.googlesource.com/platform/libcore/+/…
Nathan F.
Sim, a partir da fonte e da documentação parece que são iguais, mas na verdade System.gc()não funciona para mim, mas Runtime.getRuntime().gc()funciona!
Ivan
7

Meu aplicativo gerencia muitas imagens e morreu com um OutOfMemoryError. Isso me ajudou. No Manifest.xml Adicionar

<application
....
   android:largeHeap="true"> 
Klykoo
fonte
9
Google não gosta disso
Pedro Paulo Amorim
1
@PedroPauloAmorim explica por quê?
Machado
2
Não use heap grande a menos que tenha certeza do que está fazendo. O uso de pilhas grandes faz com que o Coletor de lixo trabalhe muito mais, porque ele precisa percorrer muitos dados inúteis antes de limpar a memória.
Prakash
7

De um modo geral, você não deve chamar GC explicitamente com System.gc (). Existe até a palestra IO ( http://www.youtube.com/watch?v=_CruQY55HOk ) onde eles explicam o que o log de pausas do GC significa e na qual eles também afirmam para nunca chamar System.gc () porque Dalvik sabe melhor do que você quando fazê-lo.

Por outro lado, como mencionado nas respostas acima, o processo de GC no Android (como tudo o mais) às vezes apresenta erros. Isso significa que os algoritmos Dalvik GC não estão no mesmo nível dos JVMs Hotspot ou JRockit e podem errar em algumas ocasiões. Uma dessas ocasiões é ao alocar objetos de bitmap. Isso é complicado porque usa memória Heap e Non Heap e porque uma instância solta de objeto de bitmap no dispositivo com restrição de memória é suficiente para fornecer uma exceção OutOfMemory. Portanto, chamá-lo depois de não precisar mais desse bitmap é geralmente sugerido por muitos desenvolvedores e até considerado uma boa prática por algumas pessoas.

A prática recomendada é usar .recycle () em um bitmap, pois é para isso que esse método foi feito, pois ele marca a memória nativa do bitmap como segura para exclusão. Tenha em mente que isso depende muito da versão, o que significa que geralmente será necessário em versões mais antigas do Android (Pre 3.0, eu acho), mas não será necessário em versões posteriores. Também não vai doer muito usá-lo em versões mais novas do ether (apenas não faça isso em um loop ou algo parecido). O novo tempo de execução do ART mudou muito aqui porque eles introduziram uma "partição" especial do Heap para objetos grandes, mas acho que não vai doer muito fazer isso com o ART ether.

Também uma observação muito importante sobre System.gc (). Este método não é um comando ao qual Dalvik (ou JVMs) são obrigados a responder. Considere isso mais como dizer à máquina virtual "Você poderia fazer a coleta de lixo se não for um incômodo".

Igor Čordaš
fonte
3

Eu diria que não, porque os documentos do desenvolvedor sobre o estado de uso de RAM :

...
GC_EXPLICIT

Um GC explícito, como quando você chama gc () (que você deve evitar chamar e, em vez disso, confiar que o GC será executado quando necessário).

...

Eu destaquei a parte relevante em negrito.

Ter um olhar para a série YouTube, do Android Performance Patterns - que irá mostrar-lhe dicas sobre o gerenciamento de uso de memória do seu aplicativo (como o uso do Android de ArrayMaps e SparseArrays em vez de HashMaps).

Farbod Salamat-Zadeh
fonte
3

Nota rápida para desenvolvedores Xamarin .

Se você gostaria de chamar System.gc()em aplicativos Xamarin.Android você deve chamarJava.Lang.JavaSystem.Gc()

Denis Gordin
fonte
2

Não há necessidade de chamar o coletor de lixo após um OutOfMemoryError.

Seu Javadoc afirma claramente:

Thrown when the Java Virtual Machine cannot allocate an object because it is out of memory, and no more memory could be made available by the garbage collector.

Portanto, o coletor de lixo já tentou liberar memória antes de gerar o erro, mas não teve êxito.

perdian
fonte
8
O Android Javadoc não afirma isso e, provavelmente, sua implementação é diferente. d.android.com/reference/java/lang/OutOfMemoryError.html
hpique
Você está correto ao dizer que isso não se aplica ao Android. Mas, novamente, você não pode lidar com OOE no tempo de execução ether, então você não pode fazer muito sobre isso, mesmo que isso fosse / não fosse verdade.
Igor Čordaš