Resumo do vazamento de memória Android
O objetivo do gerenciamento da memória é nos ajudar a evitar efetivamente vazamentos de memória em nossos aplicativos durante o desenvolvimento. Todo mundo está familiarizado com vazamentos de memória. Simplificando, significa que o objeto que deve ser liberado não foi liberado e foi mantido por uma certa instância, mas não é mais usada, para que o GC não possa ser reciclado. Recentemente, li muitos documentos e materiais relevantes. Planejo resumir e resolvê -los, compartilhar e aprender com você, e também me avisar sobre como evitar essas situações durante a codificação no futuro e melhorar a experiência e a qualidade do aplicativo.
Começarei com o básico dos vazamentos de memória Java e usarei exemplos específicos para ilustrar as várias causas dos vazamentos de memória do Android, bem como usar as ferramentas para analisar vazamentos de memória de aplicativos e, finalmente, resumi -los.
Estratégia de alocação de memória Java
Existem três tipos de estratégias de alocação de memória quando os programas Java são executados, a saber, alocação estática, alocação de pilha e alocação de heap. Da mesma forma, o espaço de memória usado pelas três estratégias de armazenamento é principalmente a área de armazenamento estático (também conhecida como área de método), área de pilha e área de heap.
Área de armazenamento estático (área do método): principalmente armazena dados estáticos, dados estáticos globais e constantes. Esta peça de memória é alocada quando o programa é compilado e existe durante todo o programa.
Área de pilha: Quando um método é executado, as variáveis locais no corpo do método (incluindo o tipo de dados básicas e a referência de objeto) são criadas na pilha, e a memória mantida por essas variáveis locais será lançada automaticamente no final da execução do método. Como a operação de alocação de memória da pilha é incorporada no conjunto de instruções do processador, é muito eficiente, mas a capacidade de memória alocada é limitada.
Área da pilha: também conhecida como alocação de memória dinâmica, geralmente refere -se à memória diretamente nova quando o programa está em execução, ou seja, uma instância do objeto. Quando essa parte da memória não estiver em uso, o Java Garbage Collector será responsável pela reciclagem.
A diferença entre pilha e heap:
Alguns tipos básicos de variáveis definidos no corpo do método e as variáveis de referência dos objetos são alocadas na memória da pilha do método. Quando uma variável é definida em um bloco de método, o Java alocará o espaço de memória para a variável na pilha. Quando o escopo da variável for excedido, a variável será inválida e o espaço de memória alocado para ela será libertado e o espaço de memória pode ser reutilizado.
A memória da pilha é usada para armazenar todos os objetos criados por novos (incluindo todas as variáveis de membros no objeto) e matrizes. A memória alocada na pilha será gerenciada automaticamente pelo coletor de lixo Java. Depois que uma matriz ou objeto é gerada na pilha, uma variável especial pode ser definida na pilha. O valor dessa variável é igual ao primeiro endereço da matriz ou objeto na memória da heap. Esta variável especial é a variável de referência mencionada acima. Podemos acessar objetos ou matrizes na pilha nessa variável de referência.
Por exemplo:
classe pública amostra {int s1 = 0; amostra msample1 = new sample (); public void method () {int s2 = 1; amostra msample2 = new sample ();}} amostra msample3 = new sample (); A variável local S2 da classe de amostra e a variável de referência msample2 existem na pilha, mas o objeto apontado pelo msample2 existe na pilha.
A entidade do objeto apontada pelo msample3 é armazenada na pilha, incluindo todas as variáveis de membro S1 e msample1 desse objeto, e ele se existe na pilha.
para concluir:
Os tipos básicos de dados e referências de variáveis locais são armazenados na pilha e as entidades de objetos referenciadas são armazenadas na pilha. - Como eles pertencem a variáveis nos métodos, o ciclo de vida termina com os métodos.
As variáveis de membros são todas armazenadas e na pilha (incluindo tipos de dados básicos, entidades de objetos referenciadas e referenciadas) - porque pertencem a classes, os objetos de classe serão usados para novo uso.
Depois de entender a alocação de memória de Java, vamos dar uma olhada em como Java gerencia a memória.
Como Java gerencia a memória
O gerenciamento de memória de Java é a questão da alocação e liberação de objetos. Em Java, os programadores precisam se inscrever para o espaço de memória para cada objeto através da palavra -chave nova (exceto para tipos básicos), e todos os objetos alocam espaço na pilha (heap). Além disso, a liberação de objetos é determinada e executada pelo GC. Em Java, a alocação de memória é feita por programas, enquanto a liberação da memória é feita pelo GC. Esse método de receita e despesa em duas linhas simplifica o trabalho dos programadores. Mas, ao mesmo tempo, também contribui para o trabalho da JVM. Essa também é uma das razões pelas quais os programas Java são mais lentos. Porque, para liberar corretamente os objetos, o GC deve monitorar o status de execução de cada objeto, incluindo o aplicativo, citação, citação, atribuição, etc. do objeto, e o GC precisa monitorá -lo.
Monitorar o estado de um objeto é liberar o objeto com mais precisão e tempo oportuno, e o princípio fundamental de liberar o objeto é que o objeto não é mais referenciado.
Para entender melhor como o GC funciona, podemos considerar o objeto como um vértice de um gráfico direcionado e o relacionamento de referência como arestas direcionadas do gráfico, que apontam do referenciador para o objeto referenciado. Além disso, cada objeto Thread pode ser usado como o vértice inicial de um gráfico. Por exemplo, a maioria dos programas começa a partir do processo principal, portanto o gráfico é uma árvore de raiz, começando com o processo principal do processo. Neste gráfico direcionado, os objetos acessíveis pelo vértice raiz são objetos válidos e o GC não reciclará esses objetos. Se um objeto (subgrafado conectado) não estiver alcançado a partir deste vértice raiz (observe que o gráfico é um gráfico direcionado), acreditamos que esse objeto (esses) não é mais referenciado e pode ser reciclado por GC.
Abaixo, damos um exemplo de como usar gráficos direcionados para representar gerenciamento de memória. Para cada momento do programa, temos um gráfico direcionado representando a alocação de memória da JVM. A figura abaixo é um diagrama do programa à esquerda, correndo para a linha 6.
O Java usa gráficos direcionados para gerenciamento de memória, que podem eliminar o problema dos loops de referência. Por exemplo, existem três objetos que se referem um ao outro. Desde que eles e o processo raiz sejam inacessíveis, o GC também poderá reciclá -los. A vantagem desse método é que ele tem alta precisão no gerenciamento da memória, mas é de baixa eficiência. Outra tecnologia de gerenciamento de memória comumente usada é usar contadores. Por exemplo, o modelo COM usa o método do contador para gerenciar componentes. Comparado aos gráficos direcionados, possui linhas de baixa precisão (é difícil lidar com problemas de referência circulares), mas possui alta eficiência de execução.
O que é um vazamento de memória em java
Em Java, os vazamentos de memória são a existência de alguns objetos alocados, que têm as duas características a seguir. Primeiro, esses objetos são acessíveis, isto é, no gráfico direcionado, existem caminhos que podem ser conectados a eles; Em segundo lugar, esses objetos são inúteis, ou seja, o programa não usará esses objetos novamente no futuro. Se o objeto atender a essas duas condições, esses objetos poderão ser determinados como um vazamento de memória em Java, e esses objetos não serão reciclados pelo GC, mas ocupa memória.
Em C ++, os vazamentos de memória têm uma faixa maior. Alguns objetos têm espaço de memória alocado, mas inacessível. Como não há GC no C ++, essas memória nunca serão coletadas. Em Java, esses objetos inacessíveis são reciclados pelo GC; portanto, os programadores não precisam considerar essa parte do vazamento de memória.
Através da análise, sabemos que, para C ++, os programadores precisam gerenciar arestas e vértices por si mesmos, enquanto para os programadores Java, eles só precisam gerenciar as bordas (sem necessidade de gerenciar a liberação de vértices). Dessa forma, o Java melhora a eficiência da programação.
Portanto, através da análise acima, sabemos que também existem vazamentos de memória em Java, mas o escopo é menor que o de C ++. Como o idioma Java garante que qualquer objeto seja acessível, todos os objetos inacessíveis são gerenciados pelo GC.
Para programadores, o GC é basicamente transparente e invisível. Embora tenhamos apenas algumas funções para acessar o GC, como o System.gc (), que executa o GC, de acordo com a definição de especificação de idiomas Java, esta função não garante que o coletor de lixo da JVM seja executado. Porque, diferentes implementadores da JVM podem usar algoritmos diferentes para gerenciar o GC. Geralmente, os threads da GC têm menor prioridade. Existem muitas estratégias para a JVM ligar para o GC. Alguns deles só começam a trabalhar quando o uso da memória atinge um certo nível. Alguns os executam regularmente. Alguns executam GC suavemente, e outros executam o GC de maneira interrompida. Mas, de um modo geral, não precisamos nos preocupar com isso. A menos que em algumas situações específicas, a execução do GC afete o desempenho do aplicativo. Por exemplo, para sistemas baseados na Web em tempo real, como jogos on-line, os usuários não querem que a GC interrompa repentinamente a execução de aplicativos e execute a coleta de lixo, então precisamos ajustar os parâmetros do GC para que a GC possa libertar a memória de maneira suave, como a decomposição da coleta de lixo em uma série de pequenos passos para executar. A JVM de hotspot fornecida pelo Sun suporta esse recurso.
Também fornece um exemplo típico de vazamento de memória Java.
Vetor v = novo vetor (10); para (int i = 1; i <100; i ++) {objeto o = new Object (); v.add (o); o = null; }Neste exemplo, solicitamos o ciclo do objeto de objeto e colocamos o objeto aplicado em um vetor. Se apenas liberarmos a própria referência, o vetor ainda faz referência ao objeto, para que esse objeto não seja reciclável para o GC. Portanto, se o objeto precisar ser excluído do vetor depois de ser adicionado ao vetor, a maneira mais fácil é definir o objeto vetorado como nulo.
Vazamento de memória em java detalhado
1. Mecanismo de reciclagem de memória java
Independentemente do método de alocação de memória de qualquer idioma, é necessário retornar o endereço real da memória alocada, isto é, retornar um ponteiro ao primeiro endereço do bloco de memória. Os objetos em Java são criados usando métodos novos ou de reflexão. A criação desses objetos é alocada na pilha. Todos os objetos são coletados pela máquina virtual Java através de um mecanismo de coleta de lixo. Para liberar corretamente os objetos, o GC monitorará o status de saúde de cada objeto e monitorará seu aplicativo, citação, citação, atribuição etc. O Java usará métodos de gráfico direcionado para gerenciar a memória para monitorar se o objeto pode ser alcançado em tempo real. Se não for alcançado, será reciclado, o que também pode eliminar o problema dos loops de referência. Na linguagem Java, existem dois tipos de espaço de memória que determina se um espaço de memória atende aos critérios de coleta de lixo: um é atribuir um valor vazio ao objeto, que não foi chamado abaixo, e o outro é atribuir um novo valor ao objeto, realocando o espaço de memória.
2. Causas de vazamento de memória Java
O vazamento de memória refere -se ao objeto inútil contínuo (objeto que não é mais usado) ou a memória de objetos inúteis não pode ser liberada no tempo, resultando em desperdício de espaço de memória, que é chamado de vazamento de memória. Os vazamentos de memória às vezes não são graves e não são fáceis de detectar, portanto, os desenvolvedores não sabem que há um vazamento de memória, mas às vezes pode ser muito sério e o solicitará a sair da memória.
Qual é a causa raiz do vazamento de memória java? Se um objeto de ciclo de vida de longa data mantiver uma referência a um objeto de ciclo de vida curto, é provável que ocorra um vazamento de memória. Embora um objeto de ciclo de vida curto não seja mais necessário, ele não pode ser reciclado porque mantém sua referência para um ciclo de longa vida. Este é o cenário em que os vazamentos de memória ocorrem em Java. Existem principalmente as seguintes categorias:
1. A classe de coleta estática causa vazamento de memória:
O uso de hashmap, vetor, etc. é mais provável que ocorra em vazamentos de memória. O ciclo de vida dessas variáveis estáticas é consistente com o da aplicação. Todos os objetos que eles fazem referência não podem ser liberados porque também serão referenciados pelo vetor, etc.
Por exemplo
Vetor estático v = novo vetor (10); para (int i = 1; i <100; i ++) {objeto o = new Object (); v.add (o); o = null;}Neste exemplo, o objeto é aplicado loop e o objeto aplicado é colocado em um vetor. Se a referência em si for libertada apenas (o = nulo), o vetor ainda faz referência ao objeto, para que esse objeto não seja reciclável para GC. Portanto, se o objeto precisar ser excluído do vetor depois de ser adicionado ao vetor, a maneira mais fácil é definir o objeto vetorado como nulo.
2. Quando as propriedades do objeto na coleção são modificadas, o método Remover () não funcionará.
Por exemplo:
public static void main (string [] args) {Set <Pesso> set = new HashSet <Soper> (); Pessoa P1 = Nova pessoa ("Tang Monk", "Pwd1", 25); Pessoa P2 = Nova pessoa ("Sun Wukong", "Pwd2", 26); Pessoa P3 = Nova pessoa ("Zhu" Bajie "," pwd3 ", 27); set.add (p1); set.add (p2); set.add (p3); system.out.println (" Há um total de: "+set.size ()+" elementos! "); // Resultado: Há um total de: 3 elementos! P3.Setrage (2); // modifica a idade de p3 e o valor de hashcode correspondente às alterações do elemento p3 neste momento definido.remove (p3); // Remova -o neste momento, causando o conjunto de vazamentos de memória.Add (P3); // Adicione -o novamente e foi adicionado com sucesso System.out.println ("Existem:"+set.size ()+"Elements!"); // Resultado: Existem: 4 elementos no total! para (pessoa de pessoa: set) {System.out.println (pessoa);}}3. ouvinte
Na programação Java, todos precisamos lidar com os ouvintes. Geralmente, muitos ouvintes são usados em um aplicativo. Chamaremos um método de controle como addxxxlistener () para adicionar ouvintes, mas muitas vezes ao liberar o objeto, não lembramos de excluir esses ouvintes, aumentando assim a chance de vazamentos de memória.
4. Várias conexões
Por exemplo, a conexão com o banco de dados (DataSourse.getConnection ()), conexão de rede (soquete) e IO não serão automaticamente reciclados pelo GC, a menos que ele chama explicitamente seu método Close () para fechar sua conexão. Os objetos do ResultSet e da declaração não podem ser explicitamente reciclados, mas a conexão deve ser explicitamente reciclada porque a conexão não pode ser reciclada automaticamente a qualquer momento. Depois que a conexão for reciclada, os objetos do ResultSet e da instrução serão imediatamente nulos. No entanto, se você usar um pool de conexões, a situação será diferente. Além de fechar explicitamente a conexão, você também deve fechar explicitamente o objeto de instrução ResultSet (fechando um deles, o outro também será fechado); caso contrário, um grande número de objetos de instrução não será divulgado, causando vazamentos de memória. Nesse caso, a conexão geralmente será liberada na tentativa e finalmente.
5. Referências a classes internas e módulos externos
As referências a classes internas são relativamente fáceis de esquecer e, uma vez lançadas, uma série de objetos de classe sucessora não pode ser lançada. Além disso, os programadores também devem ter cuidado com referências inadvertidas a módulos externos. Por exemplo, o programador A é responsável pelo módulo A e chama um método do módulo B, como:
public void Registermsg (objeto B);
Esse tipo de chamada requer muito cuidado. Quando um objeto é passado, é muito provável que o Módulo B mantenha uma referência ao objeto. No momento, você precisa prestar atenção se o Módulo B fornece operações correspondentes para remover referências.
6. Modo Singleton
O uso incorreto do padrão de singleton é um problema comum que causa vazamentos de memória. Os objetos de singleton existirão durante todo o ciclo de vida da JVM após a inicialização (na forma de variáveis estáticas). Se o objeto Singleton mantiver referências externas, esse objeto não será reciclado normalmente pela JVM, resultando em vazamentos de memória. Considere o seguinte exemplo:
classe a {public a () {b.getInstance (). seta (this);} ....} // classe B usa a classe Singleton Modo B {private a a; private estático b instância = new b (); public b () {} public static b getinstance () {retornar;Obviamente, B adota o padrão Singleton, que mantém uma referência a um objeto A, e o objeto desta classe A não será reciclado. Imagine o que aconteceria se fosse um objeto ou tipo de coleção mais complexo
Resumo dos vazamentos de memória comum no Android
Vazamento da classe de coleção
Se a classe de coleta tiver apenas um método para adicionar elementos e não tiver um mecanismo de exclusão correspondente, a memória será ocupada. Se esta classe de coleta for uma variável global (como propriedades estáticas na classe, mapa global etc., ou seja, existe uma referência estática ou apontamento final para ela o tempo todo), não há um mecanismo de exclusão correspondente, que pode causar a memória ocupada pela coleção apenas aumentar e não diminuir. Por exemplo, o exemplo típico acima é uma dessas situações. Obviamente, definitivamente não escreveremos esse código 2B no projeto, mas ainda é fácil acontecer se não tomarmos cuidado. Por exemplo, todos gostamos de fazer alguns caches através do Hashmap, por isso devemos ter mais cuidado nessa situação.
Vazamento de memória causado por singletons
Como a natureza estática de um singleton faz seu ciclo de vida enquanto o ciclo de vida do aplicativo, se usado de maneira inadequada, é fácil causar vazamento de memória. Por exemplo, o seguinte exemplo típico,
public class AppManager {private Static AppManager Instância; contexto privado Contexto; AppManager privado (contexto contexto) {this.Context = Context;} public static appManager getInstance (contexto context) {if (instance == null) {instance = new AppManager (contexto);} retornar;}}}}}Este é um padrão normal de singleton. Ao criar esse singleton, como um contexto precisa ser passado, a duração do ciclo de vida desse contexto é crucial:
1. Se o contexto da aplicação for passado neste momento, porque o ciclo de vida da aplicação é o ciclo de vida de todo o aplicativo, não haverá problema.
2. Se o contexto da atividade for passado neste momento, quando a atividade correspondente a esse contexto sai, uma vez que a referência ao contexto é mantida por um objeto Singleton, seu ciclo de vida é igual a todo o ciclo de vida do aplicativo; portanto, quando a atividade sai, sua memória não será reciclada, o que causa um vazamento.
A maneira correta deve ser alterada para o seguinte:
classe pública AppManager {private estático AppManager Instância; contexto privado Contexto; AppManager privado (contexto de contexto) {this.Context = context.getApplicationContext (); // Contexto usando o aplicativo} public static appmanager getInstance (contexto de contexto) {if (instance == null) {Instância = novo appmanager (contexto);}Ou escreva dessa maneira e você nem precisa passar o contexto em:
Adicione um método estático ao seu aplicativo, getContext () retorna o contexto do aplicativo.
...
context = getApplicationContext (); .../*** Obtenha contexto global*@return retornar objeto de contexto global*/public static context getContext () {return context;} public class AppManager {private static AppManager Instância; {instance = new AppManager ();} retornar a instância;}}Classes internas anônimas/classes internas não estáticas e tópicos assíncronos
Vazamento de memória causado pela criação de instâncias estáticas em classes internas não estáticas
Às vezes, podemos iniciar atividades com frequência. Para evitar criar repetidamente os mesmos recursos de dados, pode ocorrer essa maneira de escrever:
classe pública MainActivity estende AppCompatactivity {Private estático testResource mresource = null; @OverrideProtected void onCreate (pacote SAVEDINSTANCESTATE) {super.Oncreate (savedInstancestate); setContentView (r.Layout.Activity_Main); se (Mmanager); TestResource ();} // ...} classe testResource {// ...}}Isso cria um singleton de uma classe interna não estática dentro da atividade, e os dados do singleton são usados toda vez que a atividade é iniciada. Embora a criação repetida de recursos seja evitada, este escrito causará vazamentos de memória, porque a classe interna não estática reterá referências a classes externas por padrão, e a classe interna não estática criará uma instância estática, e o ciclo de vida da instância é que a atividade que não se refere a atividade que não se refere à atividade, que não pode se referir a atividade, que não se refere a atividade, que não se refere a atividade. A maneira correta de fazer isso é:
Defina a classe interna como uma classe interna estática ou extraia a classe interna e a encapsule em um singleton. Se você precisar usar o contexto, siga o contexto recomendado acima para usar o aplicativo. Obviamente, o contexto da aplicação não é onipotente, por isso não pode ser usado aleatoriamente. Em alguns lugares, você deve usar o contexto da atividade. Os cenários de aplicação do contexto de aplicação, serviço e atividade são os seguintes:
Onde: NO1 significa que o aplicativo e o serviço podem iniciar uma atividade, mas uma nova fila de tarefas precisa ser criada. Para diálogo, ele só pode ser criado em atividade
Classe interna anônima
O desenvolvimento do Android geralmente herda a implementação da atividade/fragmento/visão. Neste momento, se você usa aulas anônimas e é mantido por tópicos assíncronos, tenha cuidado. Se não houver medida, isso definitivamente levará ao vazamento.
classe pública MainActivity estende a atividade {... runnable ref1 = new myRunable (); runnable ref2 = new runnable () {@OverridePublic void run () {}}; ...}A diferença entre Ref1 e Ref2 é que o REF2 usa classes internas anônimas. Vamos dar uma olhada na memória referenciada em tempo de execução:
Como você pode ver, a REF1 não é nada de especial.
Mas há uma referência adicional no objeto de implementação da classe anônima Ref2:
Esta referência de US $ 0 aponta para a MainActivity.Esto, ou seja, a instância atual da MainActivity será mantida pela Ref2. Se essa referência for transmitida para um encadeamento assíncrono, e este encadeamento e esse ciclo de vida da atividade forem inconsistentes, o vazamento de atividade será causado.
Vazamento de memória causado pelo manipulador
O problema de vazamento de memória causado pelo uso do manipulador deve ser considerado o mais comum. Para evitar ANR, não executamos operações demoradas no thread principal e usamos o manipulador para lidar com tarefas de rede ou encapsular alguns retornos de chamada de solicitação e outras APIs. No entanto, o manipulador não é onipotente. Se o código do manipulador for gravado de maneira padronizada, poderá causar vazamentos de memória. Além disso, sabemos que o manipulador, a mensagem e o MessageQueue estão todos relacionados entre si. No caso de a mensagem enviada pelo manipulador ainda não ter sido processada, a mensagem e o objeto Handler que o enviaram será mantido pelo Thread MessageQueue.
Como o manipulador pertence às variáveis TLS (Thread Storage Local), o ciclo de vida e a atividade são inconsistentes. Portanto, esse método de implementação geralmente é difícil de garantir que seja consistente com o ciclo de vida da visão ou atividade, por isso é fácil causar a liberação correta.
Por exemplo:
classe pública SampleActivity estende a atividade {Manipulador final privado MleakyHandler = new Handler () {@OverridePublic void handleMessage (mensagem msg) {// ...}}@substituição de abre -sectiState (bundle SavedInStancestate) {Super.OnCreate (SavedInStaInState); MINUS.MLEAKYHANDLER.POSTDELAYED (new Runnable () {@OverridePublic void run () {/ * ... */}}, 1000 * 60 * 10); // Volte para a atividade anterior.finish ();}}Uma mensagem atrasada na execução de 10 minutos é declarada na amostragem, e o MleakyHandler o empurra para a fila de mensagens MessageQueue. Quando a atividade é descartada por Finish (), a mensagem que atrasa a execução da tarefa continuará a existir no thread principal, que mantém a referência do manipulador da atividade, para que a atividade descartada por acabamento () não seja reciclada, causando vazamento de memória (porque o manipulador é uma classe interna não estática, ela manterá referências à classe externa, que referenciam a samplina para samplasaquesas.
Correção: Evite usar classes internas não estáticas em atividade. Por exemplo, se declararmos o manipulador como estático acima, seu período de sobrevivência não tem nada a ver com o ciclo de vida da atividade. Ao mesmo tempo, a atividade é introduzida através de referências fracas para evitar a aprovação diretamente da atividade como um contexto. Veja o seguinte código:
classe pública Sampleativity estende a atividade {/*** Instâncias de classes internas estáticas não mantêm uma referência implícita*à sua classe externa. handleMessage (mensagem msg) {sampleativity Atividade = mactivity.get (); if (atividade! = null) {// ...}}} private final Myhandler mhandler = new MyHandler (this);/*** STATION de aulas anônimas não possui uma referência implícita*para a sua classe stawer quando forem "Static. {@OverridePublic void run () {/ * ... */}};@substituto void onCreate (pacote savedInsTancestate) {super.OnCreate (salvado de 10 minutos); o backing); // Publique a execução de 10 minutos. Atividades.finish ();}}Visão geral, é recomendável usar a classe interna estática + fraca referência. Tenha cuidado para estar vazio antes de cada uso.
A referência fraca foi mencionada anteriormente, então aqui vou falar brevemente sobre vários tipos de referência de objetos Java.
O Java possui quatro categorias de referências: forte referência, softreference, fraca referência e phatomreference.
No desenvolvimento de aplicativos Android, para evitar o excesso de memória, ao lidar com alguns objetos que ocupam grande memória e têm um longo ciclo de declaração, referência suave e tecnologias de referência fraca podem ser usadas o máximo possível.
Referências suaves/fracas podem ser usadas em conjunto com uma fila de referência (ReferenceQueue). Se o objeto referenciado pela referência soft for reciclado pelo coletor de lixo, a máquina virtual Java adicionará a referência suave à fila de referência associada. Esta fila permite que você conheça a lista reciclada de referências suaves/fracas, limpando assim o buffer que falhou referências suaves/fracas.
Suponha que nosso aplicativo use um grande número de imagens padrão, como o avatar padrão, o ícone do jogo padrão etc., que será usado em muitos lugares. Se você ler a imagem sempre, ela será mais lenta porque a leitura do arquivo requer operação de hardware, o que levará a um desempenho mais baixo. Por isso, consideramos o cache a imagem e a lemos diretamente da memória quando necessário. No entanto, como as imagens ocupam muito espaço de memória e o cache, muitas imagens exigem muita memória, pode ter maior probabilidade de exceções municipais. Neste momento, podemos considerar o uso de técnicas de referência suaves/fracas para evitar esse problema. A seguir, o protótipo do cache:
Primeiro defina um hashmap e salve o objeto de referência suave.
mapa privado <string, softreference <bitmap>> imagecache = new hashmap <string, softreference <bitmap>> ();
Vamos definir um método para salvar a referência suave do bitmap no hashmap.
Após o uso de referências suaves, antes que a exceção do OutOfMemory ocorra, o espaço de memória desses recursos de imagem em cache pode ser libertado, impedindo assim que a memória atinja o limite superior e evite falhas.
Se você deseja apenas evitar a ocorrência da exceção de memória, pode usar referências suaves. Se você se preocupa mais com o desempenho do seu aplicativo e deseja reciclar alguns objetos que ocupam mais memória o mais rápido possível, você pode usar referências fracas.
Além disso, você pode determinar se o objeto é freqüentemente usado para determinar se ele foi selecionado para referência suave ou referência fraca. Se o objeto puder ser usado com frequência, tente usar referências suaves. Se o objeto não for usado mais provável, poderá ser usado com referências fracas.
Ok, continue voltando ao tópico. Como mencionado anteriormente, crie uma classe interna do manipulador estático e use referências fracas aos objetos mantidos pelo manipulador, para que os objetos mantidos pelo manipulador também possam ser reciclados durante a reciclagem. No entanto, embora isso evite vazamento de atividades, ainda pode haver mensagens pendentes na fila de mensagens do thread looper, para que devemos remover as mensagens na fila de mensagens MessageQueue durante a destruição ou a parada da atividade.
Os seguintes métodos podem remover a mensagem:
RemoveCallbacks do vazio final público (Runnable r); RemoveCallbacks do vazio final público (Runnable r, token de objeto); RemoveCallsAndMessessages do vazio vazio público (token do objeto); Public Final Void RemoveMessages (int o que); Public Final Void RemoveMessages (int o que, objeto objeto);
Tente evitar o uso de variáveis estáticas de membros
Se uma variável de membro for declarada estática, todos sabemos que seu ciclo de vida será o mesmo que todo o ciclo de vida do processo do aplicativo.
Isso causará uma série de problemas. Se o seu processo de aplicativo for projetado para ser residente em memória, mesmo que o aplicativo seja cortado em segundo plano, essa parte da memória não será lançada. De acordo com o mecanismo atual de gerenciamento de memória dos aplicativos móveis, os processos em segundo plano que representam uma grande quantidade de memória serão reciclados primeiro. Se este aplicativo tiver feito proteção mútua de processos, fará com que o aplicativo reinicie com frequência em segundo plano. Quando o telefone instala o aplicativo que você participou do desenvolvimento, o telefone consome energia e tráfego durante a noite e seu aplicativo deve ser desinstalado ou silencioso pelo usuário.
A correção aqui é:
Não inicialize membros estáticos no início da classe. A inicialização preguiçosa pode ser considerada.
No projeto arquitetônico, devemos pensar se é realmente necessário fazer isso e tentar evitá -lo. Se a arquitetura precisar ser projetada assim, você terá a responsabilidade de gerenciar o ciclo de vida desse objeto.
Evite substituir a finalização ()
1. O método Finalize é executado em um momento incerto e não pode ser confiado para liberar recursos escassos. As razões para o tempo incerto são:
O tempo em que a máquina virtual chama GC é incerta
O tempo em que o tópico de daemon finalize é programado é incerto
2. O método finalize será executado apenas uma vez. Mesmo que o objeto seja ressuscitado, se o método finalizado foi executado, ele não será executado novamente quando for GC novamente. A razão é:
O objeto que contém o método Finalize gera uma referência finalizada da máquina virtual quando nova e referências ao objeto. Quando o método Finalize for executado, a referência finalizada correspondente ao objeto será liberada. Mesmo que o objeto seja ressuscitado neste momento (ou seja, referenciando o objeto com uma referência forte) e a segunda vez que é GC, pois a referência finalizada não se corresponde mais a ele, o método finalizado não será executado.
3. Um objeto que contém o método Finalize precisa passar por pelo menos duas rodadas de GC antes que ele possa ser lançado.
Vazamento de memória causado por recurso não fechado
对于使用了BraodcastReceiver,ContentObserver,File,游标Cursor,Stream,Bitmap等资源的使用,应该在Activity销毁时及时关闭或者注销,否则这些资源将不会被回收,造成内存泄漏。
一些不良代码造成的内存压力
有些代码并不造成内存泄露,但是它们,或是对没使用的内存没进行有效及时的释放,或是没有有效的利用已有的对象而是频繁的申请新内存。
por exemplo:
Bitmap 没调用recycle()方法,对于Bitmap 对象在不使用时,我们应该先调用recycle() 释放内存,然后才它设置为null. 因为加载Bitmap 对象的内存空间,一部分是java 的,一部分C 的(因为Bitmap 分配的底层是通过JNI 调用的)。 而这个recyle() 就是针对C 部分的内存释放。
构造Adapter 时,没有使用缓存的convertView ,每次都在创建新的converView。这里推荐使用ViewHolder。
Resumir
对Activity 等组件的引用应该控制在Activity 的生命周期之内; 如果不能就考虑使用getApplicationContext 或者getApplication,以避免Activity 被外部长生命周期的对象引用而泄露。
尽量不要在静态变量或者静态内部类中使用非静态外部成员变量(包括context ),即使要使用,也要考虑适时把外部成员变量置空;也可以在内部类中使用弱引用来引用外部类的变量。
对于生命周期比Activity长的内部类对象,并且内部类中使用了外部类的成员变量,可以这样做避免内存泄漏:
将内部类改为静态内部类
静态内部类中使用弱引用来引用外部类的成员变量
Handler 的持有的引用对象最好使用弱引用,资源释放时也可以清空Handler 里面的消息。比如在Activity onStop 或者onDestroy 的时候,取消掉该Handler 对象的Message和Runnable.
在Java 的实现过程中,也要考虑其对象释放,最好的方法是在不使用某对象时,显式地将此对象赋值为null,比如使用完Bitmap 后先调用recycle(),再赋为null,清空对图片等资源有直接引用或者间接引用的数组(使用array.clear() ; array = null)等,最好遵循谁创建谁释放的原则。
正确关闭资源,对于使用了BraodcastReceiver,ContentObserver,File,游标Cursor,Stream,Bitmap等资源的使用,应该在Activity销毁时及时关闭或者注销。
保持对对象生命周期的敏感,特别注意单例、静态对象、全局性集合等的生命周期。
The above is a summary of the causes of memory leaks in Java introduced to you by the editor and how to avoid memory leaks (super detailed version). Espero que seja útil para todos. Se você tiver alguma dúvida, deixe -me uma mensagem e o editor responderá a você a tempo. Muito obrigado pelo seu apoio ao site wulin.com!