Criar uma fuga de memória com Java

Acabei de ter uma entrevista e pediram-me para criar uma fuga de memória com Java. Escusado será dizer que me senti um idiota sem saber como começar a criar um.

Qual seria o exemplo?

Author: Mat Banik, 2011-06-24

30 answers

Aqui está uma boa maneira de criar uma verdadeira fuga de memória (objectos inacessíveis através da execução de código, mas ainda armazenados na memória) em Java puro:

  1. a aplicação cria um fio de longa duração (ou usa um conjunto de fios para vazar ainda mais rápido).
  2. o tópico carrega uma classe através de um carregador de classes (opcionalmente personalizado).
  3. A classe aloca um grande pedaço de memória (por exemplo new byte[1000000]), armazena uma forte referência a ela em um campo estático, e então armazena uma referência a si mesma em um ThreadLocal. A alocação da memória extra é opcional (vazar a instância da classe é suficiente), mas fará com que a fuga funcione muito mais rápido.
  4. o tópico limpa todas as referências à classe personalizada ou ao carregador de classes de onde foi carregado.
  5. Repito.
Isto funciona porque o ThreadLocal mantém uma referência ao objeto, que mantém uma referência à sua classe, que por sua vez mantém uma referência ao seu carregador de classes. O carregador de classes, por sua vez, mantém uma referência a todas as Classes que tem carregado.

(foi pior em muitas implementações JVM, especialmente antes do Java 7, porque Classes e ClassLoaders foram alocados diretamente em permgen e nunca foram GC'D em tudo. No entanto, independentemente de como a JVM lida com a descarga de classe, Um ThreadLocal ainda vai impedir que um objeto de classe seja recuperado.)

Uma variação neste padrão é a razão pela qual os recipientes de aplicação (como o Tomcat) podem vazar memória como um peneiro se você frequentemente recolocar aplicações que acontecem de usar De qualquer forma, os três locais. (Uma vez que o recipiente de aplicação usa Threads como descrito, e cada vez que você recolocar a aplicação um novo carregador de classe é usado.)

Update : dado que muitas pessoas continuam a perguntar por ele, aqui está um código de exemplo que mostra este comportamento em acção .

 1994
Author: Daniel Pryden, 2017-09-06 02:56:18

Referência de objecto imobilizado em campo estático [campo final esp]

class MemorableClass {
    static final ArrayList list = new ArrayList(100);
}

A chamar String.intern() num texto longo

String str=readString(); // read lengthy string any source db,textbox/jsp etc..
// This will place the string in memory pool from which you can't remove
str.intern();

(não fechado) fluxos abertos (ficheiro , rede, etc... )

try {
    BufferedReader br = new BufferedReader(new FileReader(inputFile));
    ...
    ...
} catch (Exception e) {
    e.printStacktrace();
}

Ligações não fechadas

try {
    Connection conn = ConnectionFactory.getConnection();
    ...
    ...
} catch (Exception e) {
    e.printStacktrace();
}

Áreas inacessíveis ao coletor de lixo da JVM , tais como a memória alocada através de métodos nativos

Em aplicações web, alguns objectos são armazenados no âmbito de Aplicação até que a aplicação seja explicitamente interrompida ou removida.

getServletContext().setAttribute("SOME_MAP", map);

Opções JVM incorrectas ou inapropriadas, tais como a opção noclassgc na IBM JDK que impede a recolha de lixo da classe não utilizada

Ver IBM JDK settings .

 1087
Author: Prashant Bhate, 2018-01-30 23:32:27

Uma coisa simples a fazer é usar uma HashSet com um incorrecto (ou inexistente) hashCode() ou equals(), e depois continuar a adicionar "duplicados". Em vez de ignorar duplicados como deveria, o conjunto só vai crescer e você não será capaz de removê-los.

Se quiser que estas teclas/elementos ruins fiquem por aí, pode usar um campo estático como

class BadKey {
   // no hashCode or equals();
   public final String key;
   public BadKey(String key) { this.key = key; }
}

Map map = System.getProperties();
map.put(new BadKey("key"), "value"); // Memory leak even if your threads die.
 405
Author: Peter Lawrey, 2011-06-24 21:50:56

Abaixo haverá um caso não-óbvio onde o Java vaza, além do caso padrão de ouvintes esquecidos, referências estáticas, chaves fictícias/modificáveis em hashmaps, ou apenas threads presos sem qualquer chance de terminar seu ciclo de vida.

  • {[[0]} - sempre vaza a corda, se a corda é uma substring, a fuga é ainda pior (o char subjacente[] também vazou) - no Java 7 substring também copia o char[], de modo que o posterior não se aplica ; @Daniel, não mas precisa de votos.
Vou concentrar-me em fios para mostrar o perigo de fios não geridos, principalmente, não quero tocar no swing.
  • Runtime.addShutdownHook e não remover... e então mesmo com removeShutdownHook devido a um bug na classe ThreadGroup em relação a threadgroup unstarted threads ele pode não ser coletado, efetivamente vazar o ThreadGroup. O JGroup tem a fuga no Coscuvilheiro.

  • Criando, mas não começando, um Thread vai para a mesma categoria que acima.

  • A Criação de um thread herda o ContextClassLoader e AccessControlContext, mais o ThreadGroup e qualquer InheritedThreadLocal, todas essas referências são potenciais vazamentos, além de toda a classes carregado pelo carregador de classe e todas as referências estáticas, e ja-ja. O efeito é especialmente visível com toda a estrutura Executor J.u.c. que possui uma interface super simples ThreadFactory, mas a maioria dos desenvolvedores não tem nenhuma pista do perigo que espreita. Também muitas bibliotecas começam threads a pedido (muitas indústrias bibliotecas populares).

  • ThreadLocal caches, esses são maus em muitos casos. Estou certo de que todos viram um pouco de caches simples baseados em ThreadLocal, bem a má notícia: se o fio continua indo mais do que o esperado a vida o ClassLoader de contexto, é um puro pequeno vazamento agradável. Não utilize bigodes Threadlocais a menos que seja realmente necessário.

  • Chamando ThreadGroup.destroy() quando o grupo de três não tem nenhum tópico em si, mas ainda mantém grupos de três filhos. Uma má fuga que evitará o grupo de três para remover de seu pai, mas todas as crianças tornam-se in-enumerável.

  • Usando WeakHashMap e o valor (in)diretamente referencia a chave. Esta é difícil de encontrar sem uma lixeira. Isso se aplica a todos os estendidos Weak/SoftReference que podem manter uma referência dura de volta ao objeto guardado.

  • Usando java.net.URL com o(S) protocolo (s) HTTP e carregando o recurso de (!). Este é especial, o KeepAliveCache cria um novo tópico no grupo de três sistemas que vaza o leitor de contexto do tópico actual. O fio é criado após o primeiro pedido quando não existe fio vivo, então você pode ter sorte ou apenas vazar. A fuga já está fixada no Java 7 e o código que cria o thread correctamente remove o leitor de classes de contexto. existem poucos mais casos (como o ImageFetcher, também fixo ) de criar tópicos semelhantes.

  • Usando InflaterInputStream a passar new java.util.zip.Inflater() no construtor (PNGImageDecoder por exemplo) e não ligar ao insuflador. Bem, Se passares no construtor com apenas [[18]}, nem pensar... E sim, chamar close() no fluxo não fecha o insuflador se for passado manualmente como parâmetro do construtor. Isto não é uma verdadeira fuga, uma vez que seria lançada pelo finalizador... quando o considerar necessário. Até aquele momento ele come tanto a memória nativa que pode causar Linux oom_killer para matar o processo com impunidade. A questão principal é que a finalização em Java é muito pouco confiável e O G1 piorou até às 7h02. Moral da história: libere recursos nativos o mais rápido possível; o finalizador é muito pobre.

  • O mesmo caso com java.util.zip.Deflater. Este é muito pior, já que Deflater tem fome de memória em Java, ou seja, usa sempre 15 bits (max) e 8 níveis de memória (9 é max) alocando várias centenas de KB de memória nativa. Felizmente, Deflater não é amplamente utilizado e, tanto quanto sei, o JDK não contém erros. Ligue sempre end() se criar manualmente um Deflater ou Inflater. A melhor parte dos dois últimos: Você não pode encontrá-los através de ferramentas normais de análise de perfis disponíveis.

(posso acrescentar mais alguns perdedores de tempo que encontrei a pedido.)

Boa sorte e fica a salvo, as fugas são más!
 237
Author: bestsss, 2016-02-16 15:52:26
A maioria dos exemplos aqui são "muito complexos". São casos extremos. Com estes exemplos, o programador cometeu um erro (como não Redefinir igual a/hashcode), ou foi mordido por uma caixa de Canto do JVM/JAVA (carga de classe com estática...). Acho que esse não é o tipo de exemplo que um entrevistador quer ou mesmo o caso mais comum. Mas há casos mais simples para fugas de memória. O coletor de lixo só liberta o que já não é referenciado. Nós como desenvolvedores Java não nos importamos sobre a memória. Distribuímo-lo quando necessário e libertamo-lo automaticamente. Fino.

Mas qualquer aplicação de longa duração tende a ter estado partilhado. Pode ser qualquer coisa, estática, singletons... Muitas vezes aplicações não triviais tendem a fazer gráficos de objetos complexos. Apenas esquecer de definir uma referência a nulo ou mais frequentemente esquecer de remover um objeto de uma coleção é suficiente para fazer uma fuga de memória.

Claro que todos os tipos de ouvintes (como ouvintes UI), baratas, ou qualquer compartilhamento de longa duração estado tendem a produzir vazamento de memória, se não manuseados corretamente. O que deve ser entendido é que este não é um caso de Canto Java, ou um problema com o coletor de lixo. É um problema de design. Nós projetamos que adicionamos um ouvinte a um objeto de longa vida, mas não removemos o ouvinte quando já não precisamos. Nós armazenamos objetos, mas não temos estratégia para removê-los do cache.

Talvez tenhamos um gráfico complexo que armazena o estado anterior que é necessário por uma computação. Mas o anterior o estado Está ligado ao estado antes e assim por diante.

Como se tivéssemos de fechar ligações SQL ou ficheiros. Precisamos definir referências adequadas para nulos e remover elementos da coleção. Teremos estratégias de cache adequadas (tamanho máximo da memória, número de elementos ou temporizadores). Todos os objetos que permitem a notificação de um ouvinte devem fornecer tanto um método addlistener quanto um removeListener. E quando estes Notificadores deixarem de ser utilizados, terão de limpar a sua lista de ouvintes. Uma memória a fuga é realmente possível e perfeitamente previsível. Não há necessidade de recursos linguísticos especiais ou casos de canto. Vazamentos de memória são ou um indicador de que algo está faltando ou até mesmo de problemas de design.
 165
Author: Nicolas Bousquet, 2018-02-13 07:27:47
A resposta depende inteiramente do que o entrevistador pensou que estava a perguntar. É possível na prática fazer vazamento de Java? Claro que é, e há muitos exemplos nas outras respostas. Mas há várias meta-perguntas que podem ter sido feitas?
    É uma implementação Java teoricamente "perfeita" vulnerável a fugas? O CANDIDATO compreende a diferença entre teoria e realidade?
  • O o candidato sabe como funciona a recolha do lixo?
  • Ou como é que a recolha de lixo funciona num caso ideal? Eles sabem que podem ligar para outras línguas através de interfaces nativas? Eles sabem vazar memória nas outras línguas? O candidato sabe sequer o que é a gestão da memória e o que se passa por trás da cena em Java?
Estou a ler a tua meta-pergunta como"Qual é a resposta que eu poderia ter usado nesta situação de entrevista". E, portanto, vou concentrar-me em habilidades de entrevista em vez de Java. Eu acredito que você tem mais chance de repetir a situação de não saber a resposta a uma pergunta em uma entrevista do que você está em um lugar de precisar saber como fazer vazamento Java. Espero que isto ajude. Uma das habilidades mais importantes que você pode desenvolver para entrevistas é aprender a ouvir ativamente as perguntas e trabalhar com o entrevistador para extrair suas intenções. Não só isto te deixa responder a pergunta deles como eles querem, mas também mostra que você tem algumas habilidades vitais de comunicação. E quando se trata de uma escolha entre muitos desenvolvedores igualmente talentosos, eu vou contratar aquele que ouve, pensa e entende antes que eles respondam todas as vezes.
 138
Author: PlayTank, 2015-01-27 12:52:33

O seguinte é um exemplo bastante inútil, se você não entender JDBC. Ou pelo menos como o JDBC espera que um desenvolvedor feche Connection, Statement e ResultSet instâncias antes de descartá-las ou perder referências a elas, em vez de confiar na implementação de finalize.

void doWork()
{
   try
   {
       Connection conn = ConnectionFactory.getConnection();
       PreparedStatement stmt = conn.preparedStatement("some query"); // executes a valid query
       ResultSet rs = stmt.executeQuery();
       while(rs.hasNext())
       {
          ... process the result set
       }
   }
   catch(SQLException sqlEx)
   {
       log(sqlEx);
   }
}

O problema com o acima é que o objeto Connection não está fechado, e, portanto, a conexão física permanecerá aberta, até que o coletor de lixo venha e veja que é inacessivel. O GC irá invocar o método finalize, mas existem drivers JDBC que não implementam o finalize, pelo menos não da mesma forma que o Connection.close é implementado. O comportamento resultante é que enquanto a memória será recuperada devido a objetos não alcançáveis sendo coletados, recursos (incluindo memória) associados com o objeto Connection podem simplesmente não ser recuperados.

Num evento em que o método Connection'S finalize não limpa tudo, pode-se realmente descobrir que o método físico a conexão com o servidor de banco de dados irá durar vários ciclos de coleta de lixo, até que o servidor de banco de dados finalmente descobre que a conexão não está viva (se ele faz), e deve ser fechada.

Mesmo que o driver JDBC implementasse {[[4]}, é possível que excepções sejam lançadas durante a finalização. O comportamento resultante é que qualquer memória associada com o objeto agora "dormente" não será recuperada, pois finalize é garantido ser invocado apenas uma vez.

A o cenário acima de encontrar exceções durante a finalização do objeto está relacionado a outro cenário que poderia possivelmente levar a uma fuga de memória-ressurreição do objeto. A ressurreição do objeto é muitas vezes feita intencionalmente, criando uma forte referência ao objeto de ser finalizado, a partir de outro objeto. Quando a ressurreição do objeto é usurpada, ela levará a um vazamento de memória em combinação com outras fontes de vazamento de memória.

Existem muitos mais exemplos que você pode evocar - tipo

  • gerir uma instância List onde só está a adicionar à lista e não a apagar dela( embora devesse estar a livrar-se dos elementos de que já não necessita), ou
  • Abrir Socket S ou File S, mas não fechá-los quando já não são necessários (semelhante ao exemplo acima envolvendo a classe Connection).
  • Não descarregue Singletons quando derruba uma aplicação Java EE. Aparentemente, o carregador de classe que carregou a classe singleton vai manter um referência à classe, e, portanto, a instância singleton nunca será coletada. Quando uma nova instância da aplicação é implantada, um novo carregador de classe é geralmente criado, e o carregador de classe anterior continuará a existir devido ao singleton.
 117
Author: Vineet Reynolds, 2016-02-16 15:38:36

Provavelmente um dos exemplos mais simples de um potencial vazamento de memória, e como evitá-lo, é a implementação do ArrayList.remover (int):

public E remove(int index) {
    RangeCheck(index);

    modCount++;
    E oldValue = (E) elementData[index];

    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index + 1, elementData, index,
                numMoved);
    elementData[--size] = null; // (!) Let gc do its work

    return oldValue;
}

Se estivesse a implementá-lo, teria pensado em Limpar o elemento array que já não é usado (elementData[--size] = null)? Essa referência pode manter um objecto enorme vivo ...

 106
Author: meriton, 2015-09-01 04:50:05
Sempre que mantém referências a objectos que já não precisa, tem uma fuga de memória. Veja Lidar com vazamentos de memória em programas Java para exemplos de como vazamentos de memória se manifestam em Java e o que você pode fazer sobre isso.
 64
Author: Bill the Lizard, 2011-06-24 16:18:07
És capaz de fazer uma fuga de memória com o sol.miscelanea.Inseguro classe. Na verdade, esta classe de serviço é usada em diferentes classes padrão (por exemplo em java.nio classes). Você não pode criar instância desta classe diretamente, mas você pode usar reflexão para fazer isso .

O código não compila no IDE do Eclipse-compila-o usando o comando {[[1]} (durante a compilação irá obter avisos)

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import sun.misc.Unsafe;


public class TestUnsafe {

    public static void main(String[] args) throws Exception{
        Class unsafeClass = Class.forName("sun.misc.Unsafe");
        Field f = unsafeClass.getDeclaredField("theUnsafe");
        f.setAccessible(true);
        Unsafe unsafe = (Unsafe) f.get(null);
        System.out.print("4..3..2..1...");
        try
        {
            for(;;)
                unsafe.allocateMemory(1024*1024);
        } catch(Error e) {
            System.out.println("Boom :)");
            e.printStackTrace();
        }
    }

}
 46
Author: stemm, 2016-01-05 08:37:35
Posso copiar a minha resposta daqui.: A maneira mais fácil de causar uma fuga de memória em Java?

" um vazamento de memória, em Ciência da Computação (ou vazamento, neste contexto), ocorre quando UM programa de computador consome memória, mas é incapaz de liberá-la de volta para o sistema operacional."(Wikipedia)

A resposta fácil é: você não pode. Java faz gestão automática de memória e irá libertar recursos que não são necessários para você. Não podes impedir que isto aconteça. Ele será sempre capaz de liberar o recurso. Em programas com gerenciamento de memória manual, isso é diferente. Você não pode obter alguma memória em C usando malloc(). Para libertar a memória, você precisa do ponteiro que malloc devolveu e chamar livre() sobre ele. Mas se você não tem mais o ponteiro (substituído, ou ultrapassado a vida), então você é, infelizmente, incapaz de libertar esta memória e, assim, você tem uma fuga de memória.

Todas as outras respostas até agora estão na minha definição, não são fugas de memória. Todos eles visam encher a memória com coisas inúteis muito depressa. Mas a qualquer momento você ainda pode dereferenciar os objetos que criou e assim libertar a memória -- > nenhuma fuga. a resposta de acconrad {[[3]} chega muito perto, embora como eu tenho que admitir uma vez que a sua solução é efetivamente apenas "bater" o coletor de lixo, forçando-o em um loop interminável).

A resposta longa é: você pode obter uma fuga de memória escrevendo uma biblioteca para Java usando a JNI, que pode ter gerenciamento de memória manual e, portanto, ter vazamentos de memória. Se liga para esta biblioteca, o teu processo java vai perder memória. Ou, você pode ter bugs no JVM, para que o JVM perca a memória. Há provavelmente bugs no JVM, pode até haver alguns conhecidos já que a coleta de lixo não é tão trivial, mas então ainda é um bug. Por projeto isso não é possível. Você pode estar pedindo algum código java que é efetuado por tal bug. Desculpe, não conheço nenhum e pode muito bem não ser mais um bug na próxima versão Java.

 40
Author: yankee, 2017-05-23 11:47:29
Aqui está uma simples e sinistra via http://wiki.eclipse.org/Performance_Bloopers#String.substring.28.29.
public class StringLeaker
{
    private final String muchSmallerString;

    public StringLeaker()
    {
        // Imagine the whole Declaration of Independence here
        String veryLongString = "We hold these truths to be self-evident...";

        // The substring here maintains a reference to the internal char[]
        // representation of the original string.
        this.muchSmallerString = veryLongString.substring(0, 1);
    }
}

Porque a substring se refere à representação interna da cadeia original, muito mais longa, a original permanece na memória. Assim, enquanto você tiver um StringLeaker em jogo, você tem toda a string original em memória, também, mesmo que você possa pensar que você está apenas segurando uma string de um único personagem.

A forma de evitar armazenar um indesejado a referência ao texto original é fazer algo assim:

...
this.muchSmallerString = new String(veryLongString.substring(0, 1));
...

Para maior maldade, também podes .intern()a substring:

...
this.muchSmallerString = veryLongString.substring(0, 1).intern();
...

Fazê-lo irá manter tanto a Cadeia Longa original como a substring derivada na memória, mesmo depois de a instância de StringLeaker ter sido descartada.

 33
Author: Jon Chambers, 2011-07-21 19:00:35

Um exemplo comum disto no código da interface é quando se cria um elemento/componente e se adiciona um ouvinte a algum objecto estático/de Aplicação escopado e depois não se remove o ouvinte quando o elemento é destruído. Não só você tem uma fuga de memória, mas também um sucesso de desempenho como quando o que quer que você está ouvindo incêndios eventos, todos os seus ouvintes velhos são chamados também.

 33
Author: pauli, 2016-02-16 15:40:24
Aceita qualquer aplicação web em qualquer recipiente de servlet (Tomcat, Jetty, Glassfish, seja o que for...). Redistribuir o aplicativo 10 ou 20 vezes em uma linha (pode ser suficiente para simplesmente tocar a guerra no diretório do servidor de autodeploy. A menos que alguém tenha testado isto, as hipóteses são altas de que você vai ter um erro grave depois de um par de recolocações, porque a aplicação não teve o cuidado de limpar depois de si mesma. Poderá até encontrar um erro no seu servidor com isto teste. O problema é que a vida útil do recipiente é maior do que a vida útil da sua aplicação. Você tem que se certificar de que todas as referências que o recipiente pode ter a objetos ou classes de sua aplicação pode ser coletado lixo.

Se houver apenas uma referência sobrevivendo ao não-funcionamento do seu aplicativo web, o classloader correspondente e, consequentemente, todas as classes do seu aplicativo web não podem ser coletadas.

Tópicos iniciados pela sua aplicação, ThreadLocal variáveis, apenders de registro são alguns dos suspeitos usuais para causar vazamentos de carregadores de classe.

 32
Author: Harald Wellmann, 2011-07-03 22:41:17
Talvez usando um código nativo externo através da JNI? Com Java puro, é quase impossível. [[1]} mas isso é sobre um tipo" padrão " de vazamento de memória, quando você não pode mais acessar a memória, mas ainda é de propriedade da aplicação. Você pode, em vez disso, manter referências a objetos não utilizados, ou córregos abertos sem fechá-los depois.
 29
Author: Rogach, 2011-06-24 21:48:48
Uma vez tive uma "fuga de memória" em relação ao PermGen e ao XML. O analisador XML que usamos (não me lembro qual era) fez uma String.estagiário() em nomes de marcas, para fazer uma comparação mais rápida. Um dos nossos clientes teve a grande ideia de armazenar valores de dados não em atributos XML ou texto, mas como tagnames, por isso tivemos um documento como:
<data>
   <1>bla</1>
   <2>foo</>
   ...
</data>
Na verdade, eles não usavam números, mas identificações textuais mais longas( cerca de 20 caracteres), que eram únicas e chegaram a uma taxa de 10-15 milhões por dia. Isso faz 200 MB de lixo por dia, o que nunca é necessário novamente, e nunca GCed (uma vez que está em PermGen). Tínhamos o permgen ajustado para 512 MB, então levou cerca de dois dias para a exceção de falta de memória (OOME) para chegar...
 28
Author: Ron, 2016-02-16 15:44:24
Recentemente encontrei uma situação de fuga de memória causada de certa forma pelo log4j.

Log4j tem este mecanismo chamado contexto de diagnóstico aninhado (NDC) que é um instrumento para distinguir a saída de log interleaved de diferentes fontes. A granularidade em que o NDC trabalha é threads, então ele distingue log saídas de diferentes threads separadamente.

Para guardar as marcas específicas do tópico, a classe NDC do log4j usa uma Hashtable que é tecida pelo próprio objecto do tópico (ao contrário de dizer o ID thread), e assim até que a tag NDC permanece na memória todos os objetos que pendem fora do objeto thread também permanecem na memória. Na nossa aplicação web, usamos NDC para marcar logoutputs com um id de pedido para distinguir logs de um único pedido separadamente. O recipiente que associa a tag NDC com um thread, também remove - o ao retornar a resposta de um pedido. O problema ocorreu quando, durante o processamento de um pedido, um fio infantil foi gerado, algo como o seguinte código:

pubclic class RequestProcessor {
    private static final Logger logger = Logger.getLogger(RequestProcessor.class);
    public void doSomething()  {
        ....
        final List<String> hugeList = new ArrayList<String>(10000);
        new Thread() {
           public void run() {
               logger.info("Child thread spawned")
               for(String s:hugeList) {
                   ....
               }
           }
        }.start();
    }
}    
Então, um contexto NDC foi associado com o fio inline que foi gerado. O objeto thread que era a chave para este contexto NDC, é o thread inline que tem o objeto hugeList pendurado fora dele. Assim, mesmo depois que o tópico terminou fazendo o que estava fazendo, a referência ao hugeList foi mantida viva pelo contexto NDC Hastable, causando assim uma fuga de memória.
 22
Author: Puneet, 2014-03-07 21:29:39
Achei interessante que ninguém usasse os exemplos internos de classe. Se você tem uma classe interna; ela inerentemente mantém uma referência à classe que contém. É claro que não é tecnicamente um vazamento de memória, porque Java vai eventualmente limpá-lo; mas isso pode causar classes de ficar por mais tempo do que o previsto.
public class Example1 {
  public Example2 getNewExample2() {
    return this.new Example2();
  }
  public class Example2 {
    public Example2() {}
  }
}
Agora se você ligar para o Example1 e obter um Example2 descartando o Example1, você inerentemente ainda terá um link para um Example1 objecto.
public class Referencer {
  public static Example2 GetAnExample2() {
    Example1 ex = new Example1();
    return ex.getNewExample2();
  }

  public static void main(String[] args) {
    Example2 ex = Referencer.GetAnExample2();
    // As long as ex is reachable; Example1 will always remain in memory.
  }
}

Eu também ouvi um boato de que se você tem uma variável que existe por mais tempo do que uma quantidade específica de tempo; Java assume que ela sempre existirá e realmente nunca vai tentar limpá-la se não puder ser alcançado em código mais. Mas isso não foi verificado.

 21
Author: Suroot, 2011-07-09 01:23:55
O que é uma fuga de memória?
    É causada por um erroou mau design. É um desperdício de memória.
  • fica pior com o tempo.
  • O coletor de lixo não pode limpá-lo.

exemplo típico:

Um cache de objectos é um bom ponto de partida para estragar as coisas.

private static final Map<String, Info> myCache = new HashMap<>();

public void getInfo(String key)
{
    // uses cache
    Info info = myCache.get(key);
    if (info != null) return info;

    // if it's not in cache, then fetch it from the database
    info = Database.fetch(key);
    if (info == null) return null;

    // and store it in the cache
    myCache.put(key, info);
    return info;
}
O teu tesouro cresce e cresce. E em breve toda a base de dados será sugada para a memória. Uma melhor utilização do design um LRUMap (só mantém objectos usados recentemente na 'cache'). Claro, podes tornar as coisas muito mais complicadas.
  • Usandoconstruções Threadlocais .
  • adicionando maisárvores de referência complexas .
  • ou fugas causadas por bibliotecas de terceiros .

O que acontece muitas vezes:

Se este objecto de informação tem referências a outros objectos, que também têm referências a outros objectos. De uma forma que você também poderia considerar isso para seja algum tipo de fuga de memória, (causada por mau design).

 20
Author: bvdb, 2014-10-28 13:18:50

Crie um mapa estático e continue adicionando referências duras a ele. Esses nunca serão GC'd.

public class Leaker {
    private static final Map<String, Object> CACHE = new HashMap<String, Object>();

    // Keep adding until failure.
    public static void addToCache(String key, Object value) { Leaker.CACHE.put(key, value); }
}
 17
Author: duffymo, 2011-07-21 17:32:58

Você pode criar uma fuga de memória em movimento criando uma nova instância de uma classe no método de finalização dessa classe. Pontos de bónus se o finalizador criar várias instâncias. Aqui está um programa simples que vaza todo o heap em algum momento entre alguns segundos e alguns minutos, dependendo do seu tamanho de heap:

class Leakee {
    public void check() {
        if (depth > 2) {
            Leaker.done();
        }
    }
    private int depth;
    public Leakee(int d) {
        depth = d;
    }
    protected void finalize() {
        new Leakee(depth + 1).check();
        new Leakee(depth + 1).check();
    }
}

public class Leaker {
    private static boolean makeMore = true;
    public static void done() {
        makeMore = false;
    }
    public static void main(String[] args) throws InterruptedException {
        // make a bunch of them until the garbage collector gets active
        while (makeMore) {
            new Leakee(0).check();
        }
        // sit back and watch the finalizers chew through memory
        while (true) {
            Thread.sleep(1000);
            System.out.println("memory=" +
                    Runtime.getRuntime().freeMemory() + " / " +
                    Runtime.getRuntime().totalMemory());
        }
    }
}
 16
Author: sethobrien, 2011-07-22 08:05:07

O entrevistador estava provavelmente à procura de uma referência circular como o código abaixo (que, por acaso, só vaza memória em JVMs muito antigos que usaram a contagem de referência, o que já não é o caso). Mas é uma pergunta bastante vaga, por isso é uma excelente oportunidade para mostrar a sua compreensão da Gestão da memória da JVM.

class A {
    B bRef;
}

class B {
    A aRef;
}

public class Main {
    public static void main(String args[]) {
        A myA = new A();
        B myB = new B();
        myA.bRef = myB;
        myB.aRef = myA;
        myA=null;
        myB=null;
        /* at this point, there is no access to the myA and myB objects, */
        /* even though both objects still have active references. */
    } /* main */
}

Então você pode explicar que com a contagem de referência, o código acima iria vazar memória. Mas a maioria das JVMs modernas não usam mais a contagem de referências., a maioria usa um coletor de lixo varredura, que de fato vai coletar esta memória.

Em seguida, você pode explicar a criação de um objeto que tem um recurso nativo subjacente, como este:

public class Main {
    public static void main(String args[]) {
        Socket s = new Socket(InetAddress.getByName("google.com"),80);
        s=null;
        /* at this point, because you didn't close the socket properly, */
        /* you have a leak of a native descriptor, which uses memory. */
    }
}

Então você pode explicar isso é tecnicamente uma fuga de memória, mas realmente a fuga é causada pelo código nativo no JVM alocando recursos nativos subjacentes, que não foram liberados pelo seu código Java.

No final do dia, com um JVM moderno, você precisa escrever algum código Java que aloca um nativo recurso fora do âmbito normal da sensibilização da JVM.
 16
Author: deltamind106, 2017-10-13 19:09:24
Todos se esquecem da rota do código nativo. Aqui está uma fórmula simples para uma fuga:
    Declare o método nativo.
  1. em método nativo, chama malloc. Não ligues.
  2. Chama o método nativo.
Lembrem-se, as alocações de memória em código nativo vêm do Monte JVM.
 15
Author: Paul Morie, 2011-07-21 20:52:40
Deparei-me com uma fuga de recursos mais subtil recentemente. Abrimos recursos através do getresourceaasstream do carregador de classe E aconteceu que os cabos do fluxo de entrada não foram fechados. Pode dizer-se que é um idiota.

Bem, o que torna isto interessante é: desta forma, você pode vazar memória heap do processo subjacente, em vez de a partir do heap da JVM.

Só precisa de um ficheiro jar com um ficheiro dentro do qual será referenciado a partir do código Java. Quanto maior o frasco file, a memória mais rápida é alocada.

Você pode facilmente criar tal frasco com a seguinte classe:

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

public class BigJarCreator {
    public static void main(String[] args) throws IOException {
        ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(new File("big.jar")));
        zos.putNextEntry(new ZipEntry("resource.txt"));
        zos.write("not too much in here".getBytes());
        zos.closeEntry();
        zos.putNextEntry(new ZipEntry("largeFile.out"));
        for (int i=0 ; i<10000000 ; i++) {
            zos.write((int) (Math.round(Math.random()*100)+20));
        }
        zos.closeEntry();
        zos.close();
    }
}
Basta colar num ficheiro chamado BigJarCreator.java, compile e execute a partir da linha de comandos:
javac BigJarCreator.java
java -cp . BigJarCreator

Et voilà: você encontra um arquivo jar na sua pasta de trabalho actual com dois ficheiros lá dentro.

Vamos criar uma segunda classe.
public class MemLeak {
    public static void main(String[] args) throws InterruptedException {
        int ITERATIONS=100000;
        for (int i=0 ; i<ITERATIONS ; i++) {
            MemLeak.class.getClassLoader().getResourceAsStream("resource.txt");
        }
        System.out.println("finished creation of streams, now waiting to be killed");

        Thread.sleep(Long.MAX_VALUE);
    }

}

Esta classe basicamente não faz nada, mas cria objetos InputStream não referenciados. Esses objectos serão lixo. coletado imediatamente e, portanto, não contribuir para o tamanho do heap. É importante para o nosso exemplo para carregar um recurso existente a partir de um arquivo jar, e o tamanho importa aqui!

Se tiver dúvidas, tente compilar e iniciar a classe acima, mas certifique-se de escolher um tamanho de heap decente (2 MB):

javac MemLeak.java
java -Xmx2m -classpath .:big.jar MemLeak

Não irá encontrar aqui um erro de OOM, uma vez que não são mantidas referências, a aplicação continuará a correr independentemente do tamanho que tenha escolhido as iterações no exemplo acima. Memoria do seu processo (visível no topo (RES/RSS) ou explorador de processo) cresce a menos que a aplicação chegue ao comando de espera. Na configuração acima, ele irá alocar cerca de 150 MB em memória.

Se quiser que a aplicação jogue pelo seguro, feche o fluxo de entrada onde foi criado:

MemLeak.class.getClassLoader().getResourceAsStream("resource.txt").close();

E o seu processo não excederá 35 MB, independentemente da contagem de iterações.

Muito simples e surpreendente.
 15
Author: Jay, 2012-09-12 20:07:37

Eu não acho que ninguém tenha dito isso ainda: você pode ressuscitar um objeto, sobrepondo o método finalize() tal que finalize() armazena uma referência disso em algum lugar. O coletor de lixo só será chamado uma vez sobre o objeto para depois que o objeto nunca será destruído.

 14
Author: Ben, 2011-07-03 17:36:58
Como muitas pessoas sugeriram, vazamentos de recursos são bastante fáceis de causar - como os exemplos do JDBC. Vazamentos de memória reais são um pouco mais difícil-especialmente se você não está confiando em pedaços quebrados do JVM para fazê-lo para você... As ideias de criar objetos que têm uma pegada muito grande e não serem capazes de acessá-los também não são vazamentos de memória reais. Se nada pode acessá-lo então será lixo coletado, e se algo pode acessá - lo então não é um vazamento...

Uma maneira que usou para trabalhar no entanto - e não sei se ainda funciona - é ter uma corrente circular de três profundidades. Como em Um Objeto possui uma referência para um Objeto B, o Objeto B tem uma referência para o Objeto C e o Objeto C tem uma referência para o Objeto A. O GC foi inteligente o suficiente para saber que um dois profunda cadeia - como em Um B - pode ser recolhidas, se A e B não são acessíveis por qualquer outra coisa, mas não conseguia lidar com o three-way cadeia...

 14
Author: Graham, 2011-07-21 17:55:51
Há muitas situações diferentes que a memória vai vazar. Um que encontrei, que expõe um mapa que não deve ser exposto e usado em outro lugar.
public class ServiceFactory {

private Map<String, Service> services;

private static ServiceFactory singleton;

private ServiceFactory() {
    services = new HashMap<String, Service>();
}

public static synchronized ServiceFactory getDefault() {

    if (singleton == null) {
        singleton = new ServiceFactory();
    }
    return singleton;
}

public void addService(String name, Service serv) {
    services.put(name, serv);
}

public void removeService(String name) {
    services.remove(name);
}

public Service getService(String name, Service serv) {
    return services.get(name);
}

// the problematic api, which expose the map.
//and user can do quite a lot of thing from this api.
//for example, create service reference and forget to dispose or set it null
//in all this is a dangerous api, and should not expose 
public Map<String, Service> getAllServices() {
    return services;
}

}

// resource class is a heavy class
class Service {

}
 11
Author: Ben Xu, 2011-07-10 07:46:03

Os fios não são recolhidos até terminarem. Eles servem como raízes da coleta de lixo. Eles são um dos poucos objetos que não serão recuperados simplesmente esquecendo-se deles ou limpando referências a eles.

Considere: o padrão básico para terminar um tópico de trabalho é definir alguma variável de condição vista pelo tópico. A thread pode verificar a variável periodicamente e usar isso como um sinal para terminar. Se a variável não for declarada volatile, então a alteração para a variável pode não ser vista pelo fio, por isso não saberá terminar. Ou imagine se alguns tópicos querem atualizar um objeto compartilhado, mas deadlock ao tentar bloquear nele.

Se você só tem um punhado de tópicos estes bugs provavelmente serão óbvios porque o seu programa vai parar de funcionar corretamente. Se você tiver um conjunto de threads que cria mais threads conforme necessário, então os threads obsoletos/presos podem não ser notados, e irão acumular indefinidamente, causando uma fuga de memória. Os tópicos são é provável que use outros dados em sua aplicação, assim também irá impedir qualquer coisa que eles diretamente referenciar de alguma vez ser coletado.

Como exemplo de brinquedo:

static void leakMe(final Object object) {
    new Thread() {
        public void run() {
            Object o = object;
            for (;;) {
                try {
                    sleep(Long.MAX_VALUE);
                } catch (InterruptedException e) {}
            }
        }
    }.start();
}
Chama o que quiseres, mas o objecto que passou para leakMe nunca morrerá.

(*editado*)

 11
Author: Boann, 2017-05-23 12:02:58

Eu acho que um exemplo válido poderia ser usar variáveis Threadlocais em um ambiente onde threads são agrupados.

Por exemplo, usando variáveis Threadlocais em Servlets para se comunicar com outros componentes da web, tendo os threads sendo criados pelo recipiente e mantendo os inactivos em um pool. Variáveis threadlocais, se não forem corretamente limpas, viverão lá até, possivelmente, o mesmo componente Web sobrepor seus valores.

Claro que, uma vez identificado, o o problema pode ser resolvido facilmente.

 9
Author: mschonaker, 2011-07-03 06:49:51

O entrevistador pode estar à procura de uma solução circular de referência:

    public static void main(String[] args) {
        while (true) {
            Element first = new Element();
            first.next = new Element();
            first.next.next = first;
        }
    }
Este é um problema clássico com a contagem de referência de coletores de lixo. Você então explicaria educadamente que o JVMs usa um algoritmo muito mais sofisticado que não tem essa limitação. - Wes Tarle.
 9
Author: Wesley Tarle, 2011-07-04 15:06:58