É possível forçar a coleta de lixo em Java, mesmo que seja complicado? Eu sei sobre System.gc();
e Runtime.gc();
mas eles apenas sugerem fazer GC. Como posso forçar o GC?
É possível forçar a coleta de lixo em Java, mesmo que seja complicado? Eu sei sobre System.gc();
e Runtime.gc();
mas eles apenas sugerem fazer GC. Como posso forçar o GC?
Respostas:
Sua melhor opção é chamar o System.gc()
que simplesmente é uma dica para o coletor de lixo que você deseja que ele faça uma coleção. Não há como forçar e coletar imediatamente, pois o coletor de lixo não é determinístico.
non-deterministic == trouble
GC.Collect()
não coleta. Em Java gc()
faz.
A biblioteca jlibs possui uma boa classe de utilitário para coleta de lixo . Você pode forçar a coleta de lixo usando um truque bacana com objetos WeakReference .
RuntimeUtil.gc () dos jlibs:
/**
* This method guarantees that garbage collection is
* done unlike <code>{@link System#gc()}</code>
*/
public static void gc() {
Object obj = new Object();
WeakReference ref = new WeakReference<Object>(obj);
obj = null;
while(ref.get() != null) {
System.gc();
}
}
PhantomReference
com a ReferenceQueue
e você seria notificado após a finalização, mas ainda antes da limpeza. Por fim, mesmo que você tenha detectado com êxito que a memória desse objeto foi recuperada, isso ainda significaria muito pouco em um GC geracional como o HotSpot. Geralmente coincidiria com a limpeza da geração jovem.
System.gc(); System.gc();
, mas com certeza seria interessante saber se funcionou melhor do que isso. De fato, apenas imprimir quantas vezes ele chamou System.gc()
seria suficiente. A chance de chegar a 2 é bastante pequena.
A melhor maneira (se não apenas) de forçar um GC seria escrever uma JVM customizada. Eu acredito que os coletores de lixo são conectáveis, então você provavelmente pode escolher uma das implementações disponíveis e ajustá-la.
Nota: Esta NÃO é uma resposta fácil.
Usando a interface da Java Virtual Machine Tool (JVM TI) , a função
jvmtiError ForceGarbageCollection(jvmtiEnv* env)
"Forçará a VM a executar uma coleta de lixo". A TI da JVM faz parte da JavaDA Platform Debugger Architecture (JPDA) .
SIM , é quase possível forçar você a chamar métodos na mesma ordem e, ao mesmo tempo, são:
System.gc ();
System.runFinalization ();
mesmo que seja apenas um objeto para limpar o uso desses dois métodos ao mesmo tempo, force o coletor de lixo a usar o finalise()
método de objeto inacessível, liberando a memória atribuída e fazendo o que o finalize()
método declara.
No entanto , é uma péssima prática usar o coletor de lixo porque o uso dele pode introduzir uma sobrecarga no software que pode ser ainda pior do que na memória, o coletor de lixo tem seu próprio encadeamento que não é possível controlar mais, dependendo da o algoritmo usado pelo gc pode levar mais tempo e é considerado muito ineficiente; você deve verificar o seu software se for pior com a ajuda do gc, pois está definitivamente quebrado, uma boa solução não deve depender do gc.
NOTA: para ter em mente, isso funcionará apenas se no método finalizar não houver uma reatribuição do objeto; se isso acontecer, o objeto permanecerá vivo e haverá uma ressurreição tecnicamente possível.
gc()
é apenas uma dica para executar uma coleta de lixo. runFinalizers()
somente executa finalizadores em objetos "que foram encontrados como descartados". Se o gc não chegou a executar, pode haver nenhum desses objetos ...
Sob a documentação de OutOfMemoryError , declara que não será lançada, a menos que a VM falhe ao recuperar a memória após uma coleta de lixo completa. Portanto, se você continuar alocando memória até obter o erro, já terá forçado uma coleta de lixo completa.
Presumivelmente, a pergunta que você realmente queria fazer era "como recuperar a memória que acho que deveria recuperar pela coleta de lixo?"
Para solicitar manualmente o GC (não do System.gc ()):
.gc é candidato à eliminação em versões futuras - um engenheiro da Sun comentou uma vez que talvez menos de vinte pessoas no mundo realmente sabem como usar .gc () - eu trabalhei ontem à noite por algumas horas em uma central / crítica estrutura de dados usando dados gerados pelo SecureRandom, em algum lugar além dos 40.000 objetos, a vm diminuiria a velocidade como se tivesse ficado sem ponteiros. Claramente, ele estava afogado em tabelas de ponteiros de 16 bits e exibia o comportamento clássico de "máquinas com falha".
Eu tentei -Xms e assim por diante, continuei mexendo um pouco até chegar a 57, xxx alguma coisa. Depois, o gc passaria de digamos 57.127 para 57.128 depois de um gc () - mais ou menos no ritmo do inchaço do código no campo Easy Money.
Seu design precisa de um retrabalho fundamental, provavelmente uma abordagem de janela deslizante.
Você pode acionar um GC na linha de comando. Isso é útil para batch / crontab:
jdk1.7.0/bin/jcmd <pid> GC.run
Vejo :
A especificação da JVM não diz nada específico sobre coleta de lixo. Devido a isso, os fornecedores são livres para implementar o GC à sua maneira.
Portanto, essa imprecisão causa incerteza no comportamento da coleta de lixo. Você deve verificar os detalhes da JVM para conhecer as abordagens / algoritmos de coleta de lixo. Também há opções para personalizar o comportamento também.
Se você precisar forçar a coleta de lixo, talvez deva considerar como está gerenciando recursos. Você está criando objetos grandes que persistem na memória? Você está criando objetos grandes (por exemplo, classes de gráficos) que possuem uma Disposable
interface e não chamam dispose()
quando terminam? Você está declarando algo em um nível de classe que você só precisa em um único método?
Seria melhor se você descrevesse o motivo pelo qual você precisa de coleta de lixo. Se você estiver usando SWT, poderá dispor de recursos como Image
e Font
para liberar memória. Por exemplo:
Image img = new Image(Display.getDefault(), 16, 16);
img.dispose();
Existem também ferramentas para determinar recursos não dispostos.
Se você está ficando sem memória e obtendo um, OutOfMemoryException
pode tentar aumentar a quantidade de espaço de heap disponível para java iniciando o programa em java -Xms128m -Xmx512m
vez de apenas java
. Isso fornecerá um tamanho de pilha inicial de 128Mb e um máximo de 512Mb, que é muito mais do que os 32Mb / 128Mb padrão.
java -Xms512M -Xmx1024M
Outra opção é não criar novos objetos.
O pool de objetos está ausente para reduzir a necessidade de GC em Java.
O pool de objetos geralmente não será mais rápido que a criação de objetos (especialmente para objetos leves), mas é mais rápido que a coleta de lixo. Se você criou 10.000 objetos e cada objeto tinha 16 bytes. São 160.000 bytes que o GC precisa recuperar. Por outro lado, se você não precisar de todos os 10.000 ao mesmo tempo, poderá criar um pool para reciclar / reutilizar os objetos, o que elimina a necessidade de construir novos objetos e elimina a necessidade de objetos antigos do GC.
Algo assim (não testado). E se você deseja que ele seja seguro para threads, você pode trocar o LinkedList por um ConcurrentLinkedQueue.
public abstract class Pool<T> {
private int mApproximateSize;
private LinkedList<T> mPool = new LinkedList<>();
public Pool(int approximateSize) {
mApproximateSize = approximateSize;
}
public T attain() {
T item = mPool.poll();
if (item == null) {
item = newInstance();
}
return item;
}
public void release(T item) {
int approxSize = mPool.size(); // not guaranteed accurate
if (approxSize < mApproximateSize) {
recycle(item);
mPool.add(item);
} else if (approxSize > mApproximateSize) {
decommission(mPool.poll());
}
}
public abstract T newInstance();
public abstract void recycle(T item);
public void decommission(T item) { }
}
No OracleJDK 10 com G1 GC, uma única chamada System.gc()
fará com que o GC limpe a coleção antiga. Não tenho certeza se o GC é executado imediatamente. No entanto, o GC não limpará a coleção Young, mesmo que System.gc()
seja chamado várias vezes em um loop. Para que o GC limpe a Coleção Jovem, você deve alocar em um loop (por exemplo new byte[1024]
) sem chamar System.gc()
. Ligar System.gc()
por algum motivo impede que o GC limpe a Coleção Young.
Realmente, eu não entendo você. Mas, para ser claro sobre a "Criação de Objetos Infinitos", eu quis dizer que há um pedaço de código no meu grande sistema que cria objetos que manipulam e estão vivos na memória.
Isso está correto, apenas gesto. Você tem praticamente as respostas padrão já dadas por vários pôsteres. Vamos pegar um por um:
Correto, não existe jvm real - isso é apenas uma especificação, um monte de ciência da computação descrevendo o comportamento desejado ... Recentemente, procurei inicializar objetos Java a partir de código nativo. Para conseguir o que deseja, a única maneira é fazer o que é chamado de nulo agressivo. Os erros cometidos de maneira errada são tão ruins que precisamos nos limitar ao escopo original da pergunta:
A maioria dos pôsteres aqui assumirá que você está dizendo que está trabalhando em uma interface, caso seja necessário, teremos que verificar se você está recebendo o objeto inteiro ou um item de cada vez.
Se você não precisar mais de um objeto, poderá atribuir nulo ao objeto, mas se errar, será gerada uma exceção de ponteiro nulo. Aposto que você pode conseguir um trabalho melhor se usar o NIO
Sempre que você ou eu ou qualquer outra pessoa obtém: " Por favor, preciso disso horrivelmente. ", É quase um precursor quase universal da destruição quase total do que você está tentando trabalhar ... escreva-nos um pequeno código de exemplo, higienizando-o código real usado e nos mostre sua pergunta.
Não fique frustrado. Muitas vezes, o que isso resolve é que o seu dba está usando um pacote comprado em algum lugar e o design original não é aprimorado para estruturas de dados maciças.
Isso é muito comum.
Para sua informação
A chamada de método System.runFinalizersOnExit (true) garante que os métodos do finalizador sejam chamados antes que o Java seja encerrado. No entanto, esse método é inerentemente inseguro e foi preterido. Uma alternativa é adicionar "ganchos de desligamento" com o método Runtime.addShutdownHook.
Masarrat Siddiqui
Existe alguma maneira indireta de forçar o coletor de lixo. Você só precisa preencher a pilha com objetos temporários até o momento em que o coletor de lixo será executado. Eu criei uma classe que força o coletor de lixo desta maneira:
class GarbageCollectorManager {
private static boolean collectionWasForced;
private static int refCounter = 0;
public GarbageCollectorManager() {
refCounter++;
}
@Override
protected void finalize() {
try {
collectionWasForced = true;
refCounter--;
super.finalize();
} catch (Throwable ex) {
Logger.getLogger(GarbageCollectorManager.class.getName()).log(Level.SEVERE, null, ex);
}
}
public int forceGarbageCollection() {
final int TEMPORARY_ARRAY_SIZE_FOR_GC = 200_000;
int iterationsUntilCollected = 0;
collectionWasForced = false;
if (refCounter < 2)
new GarbageCollectorManager();
while (!collectionWasForced) {
iterationsUntilCollected++;
int[] arr = new int[TEMPORARY_ARRAY_SIZE_FOR_GC];
arr = null;
}
return iterationsUntilCollected;
}
}
Uso:
GarbageCollectorManager manager = new GarbageCollectorManager();
int iterationsUntilGcExecuted = manager.forceGarbageCollection();
Eu não sei o quanto esse método é útil, porque ele preenche a pilha constantemente, mas se você tiver um aplicativo de missão crítica que DEVE forçar o GC - quando essa pode ser a maneira portátil em Java de forçar o GC.
Eu gostaria de adicionar alguma coisa aqui. Por favor, não que o Java seja executado na Máquina Virtual e não na Máquina real. A máquina virtual possui seu próprio meio de comunicação com a máquina. Pode variar de sistema para sistema. Agora, quando chamamos o GC, pedimos à Máquina Virtual de Java que chame o Garbage Collector.
Como o Garbage Collector está com a Máquina Virtual, não podemos forçá-lo a fazer uma limpeza lá e depois. Em vez disso, enfileiramos nossa solicitação com o Garbage Collector. Depende da Máquina Virtual, após um determinado período de tempo (isso pode mudar de sistema para sistema, geralmente quando a memória de limite alocada para a JVM estiver cheia), a máquina real liberará o espaço. : D
O código a seguir é retirado do método assertGC (...). Ele tenta forçar o coletor de lixo não determinístico a coletar.
List<byte[]> alloc = new ArrayList<byte[]>();
int size = 100000;
for (int i = 0; i < 50; i++) {
if (ref.get() == null) {
// Test succeeded! Week referenced object has been cleared by gc.
return;
}
try {
System.gc();
} catch (OutOfMemoryError error) {
// OK
}
try {
System.runFinalization();
} catch (OutOfMemoryError error) {
// OK
}
// Approach the jvm maximal allocatable memory threshold
try {
// Allocates memory.
alloc.add(new byte[size]);
// The amount of allocated memory is increased for the next iteration.
size = (int)(((double)size) * 1.3);
} catch (OutOfMemoryError error) {
// The amount of allocated memory is decreased for the next iteration.
size = size / 2;
}
try {
if (i % 3 == 0) Thread.sleep(321);
} catch (InterruptedException t) {
// ignore
}
}
// Test failed!
// Free resources required for testing
alloc = null;
// Try to find out who holds the reference.
String str = null;
try {
str = findRefsFromRoot(ref.get(), rootsHint);
} catch (Exception e) {
throw new AssertionFailedErrorException(e);
} catch (OutOfMemoryError err) {
// OK
}
fail(text + ":\n" + str);
Fonte (adicionei alguns comentários para maior clareza): Exemplo NbTestCase
Você pode tentar usar Runtime.getRuntime().gc()
ou usar o método utilitário System.gc()
Nota: Esses métodos não garantem o GC. E seu escopo deve ser limitado à JVM em vez de manipulá-lo programaticamente em seu aplicativo.