Quais ferramentas e métodos do Android funcionam melhor para encontrar vazamentos de memória / recursos? [fechadas]

152

Eu desenvolvi um aplicativo para Android e estou no ponto de desenvolvimento de um aplicativo para telefone em que tudo parece estar funcionando bem e você deseja declarar vitória e sucesso, mas você sabe que deve haver algum vazamento de memória e recursos lá; e há apenas 16 MB de pilha no Android e é aparentemente surpreendentemente fácil vazar em um aplicativo Android.

Eu estive olhando ao redor e até agora só consegui descobrir informações sobre 'hprof' e 'traceview' e nem recebe muitas críticas favoráveis.

Quais ferramentas ou métodos você encontrou ou desenvolveu e gostaria de compartilhar talvez em um projeto de sistema operacional?

jottos
fonte
3
Posso votar para que Р̀СТȢѸ́ФХѾЦЧШЩЪЫЬѢѤЮѦѪѨѬѠѺѮѰѲѴ mude seu nome para algo legível por humanos.
JPM 11/02
1
seria bom reabrir esta pergunta se removermos a palavra "Ferramentas Android" do título da pergunta? As respostas encontrado aqui foram bastante adequado para o resolver meus problemas de vazamento de memória
k3b
Ok, então eu tenho um usuário alternando constantemente entre as atividades, o que significa que eles podem ter alternado 15 atividades em 20 segundos. Isso poderia ser a causa de um erro de falta de memória? O que devo fazer para corrigi-lo? Obrigado!
Ruchir Baronia
1
Não é possível fornecer isso como resposta porque a pergunta foi encerrada, no entanto, recomendo dar uma olhada no Leak Canary . Basta usar seu aplicativo, abrir e fechar atividades e deixar a biblioteca fazer seu trabalho. Ele até informa sobre onde ocorreu o vazamento. Apenas dê algum tempo ao analisador de vazamentos para realizar seu trabalho após o vazamento - normalmente leva cerca de 2 minutos ou mais até que a fonte do vazamento seja encontrada. Depois disso, ele será apresentado perfeitamente no aplicativo. Não são necessárias ferramentas extras!
Ubuntudroid

Respostas:

90

Um dos erros mais comuns que encontrei no desenvolvimento de aplicativos Android é o erro "java.lang.OutOfMemoryError: tamanho do bitmap excede o orçamento da VM". Encontrei esse erro com freqüência em atividades usando muitos bitmaps após alterar a orientação: a atividade é destruída, criada novamente e os layouts são "inflados" a partir do XML que consome a memória da VM disponível para bitmaps.

Os bitmaps no layout de atividade anterior não são desalocados adequadamente pelo coletor de lixo porque eles cruzaram as referências à sua atividade. Depois de muitas experiências, encontrei uma solução muito boa para esse problema.

Primeiro, defina o atributo "id" na visualização pai do seu layout XML:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="fill_parent"
     android:layout_height="fill_parent"
     android:id="@+id/RootView"
     >
     ...

Em seguida, no método onDestroy () da sua Activity, chame o método unbindDrawables () passando uma referência para a View pai e, em seguida, faça um System.gc ()

@Override
protected void onDestroy() {
    super.onDestroy();

    unbindDrawables(findViewById(R.id.RootView));
    System.gc();
}


private void unbindDrawables(View view) {

    if (view.getBackground() != null) {
        view.getBackground().setCallback(null);
    }

    if (view instanceof ViewGroup) {
        for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
            unbindDrawables(((ViewGroup) view).getChildAt(i));
        }

        ((ViewGroup) view).removeAllViews();
    }
}

Este método unbindDrawables () explora a árvore de exibição recursivamente e:

  1. Remove retornos de chamada em todos os drawables em segundo plano
  2. Remove childs em todos os grupos de visualizações
hp.android
fonte
3
Boa solução para um problema comum.
Enquanto-E
9
Isso não funciona para subclasses de AdapterView (ListView, GridView etc).
Arjun
@Arjun Sim ... não funciona para as subclasses do AdapterView. Para isso, você precisa lidar com exceção. Descanse, funciona bem. É isso que eu uso no meu código e funciona bem. Espero que isto ajude.
Hp.android 21/11
@ hp.android Ao "lidar com isso na exceção", você tem um exemplo de como isso seria? Eu pergunto porque eu tenho páginas de imagens na minha PageAdaptere eu estou ficando trabalhada por este erro :(
Jacksonkr
4
@Jackson apenas mudar a condição: if (vista instanceof ViewGroup && (vista instanceof AdapterView)!), Este vai se livrar de exceção que você está recebendo para o adaptador
hp.android
28

Principalmente para os viajantes do Google no futuro:

Infelizmente, a maioria das ferramentas java não é adequada para essa tarefa, porque elas analisam apenas o JVM-Heap. Porém, todo aplicativo Android também possui uma pilha nativa, que também deve se ajustar ao limite de ~ 16 MB. Geralmente é usado para dados de bitmap, por exemplo. Portanto, você pode executar com facilidade os erros de Falta de Memória, mesmo que sua JVM-Heap esteja em torno de 3 MBs, se você usar muitas gavetas.

Timo Ohr
fonte
6
começando Android 3.0 (honeycomb) drawables são armazenados na pilha
Gu1234
@Timo, o que você usaria para detectar vazamentos na pilha nativa?
sydd
1
Testes, muitos testes. O problema é que você nem consegue dizer quanta memória seu aplicativo está usando, devido ao compartilhamento de memória e outras técnicas de otimização. Você pode obter leituras de memória usando os comandos usuais do shell, mas essas são estimativas muito, muito aproximadas.
Timo Ohr
20

A resposta do @ hp.android funciona bem se você estiver trabalhando apenas com planos de fundo de bitmap, mas, no meu caso, eu forneci BaseAdapterum conjunto de ImageViews para a GridView. Modifiquei o unbindDrawables()método conforme recomendado para que a condição seja:

if (view instanceof ViewGroup && !(view instanceof AdapterView)) {
  ...
}

mas o problema é que o método recursivo nunca processa os filhos do AdapterView. Para resolver isso, fiz o seguinte:

if (view instanceof ViewGroup) {
  ViewGroup viewGroup = (ViewGroup) view;
  for (int i = 0; i < viewGroup.getChildCount(); i++)
    unbindDrawables(viewGroup.getChildAt(i));

  if (!(view instanceof AdapterView))
    viewGroup.removeAllViews();
}

para que os filhos do AdapterViewainda sejam processados ​​- o método simplesmente não tenta remover todos os filhos (o que não é suportado).

No entanto, isso não resolve o problema, já que ImageViews gerenciamos um bitmap que não é o fundo deles. Eu, portanto, adicionei o seguinte. Não é o ideal, mas funciona:

if (view instanceof ImageView) {
  ImageView imageView = (ImageView) view;
  imageView.setImageBitmap(null);
}

No geral, o unbindDrawables()método é então:

private void unbindDrawables(View view) {
  if (view.getBackground() != null)
    view.getBackground().setCallback(null);

  if (view instanceof ImageView) {
    ImageView imageView = (ImageView) view;
    imageView.setImageBitmap(null);
  } else if (view instanceof ViewGroup) {
    ViewGroup viewGroup = (ViewGroup) view;
    for (int i = 0; i < viewGroup.getChildCount(); i++)
    unbindDrawables(viewGroup.getChildAt(i));

    if (!(view instanceof AdapterView))
      viewGroup.removeAllViews();
  }
}

Espero que exista uma abordagem mais baseada em princípios para liberar esses recursos.

darrenp
fonte
12

Boa palestra de E / S do Google (2011) sobre Gerenciamento de memória no Android, além de detalhes sobre ferramentas + técnicas para criação de perfil de memória:
http://www.youtube.com/watch?v=_CruQY55HOk

greg7gkb
fonte
4
Ou o correspondente post: android-developers.blogspot.com/2011/03/...
greg7gkb
1
Ok, então eu tenho um usuário alternando constantemente entre as atividades, o que significa que eles podem ter alternado 15 atividades em 20 segundos. Isso poderia ser a causa de um erro de falta de memória? O que devo fazer para corrigi-lo? Obrigado!'
Ruchir Baronia
1

Bem, essas são as ferramentas que combinam com os formatos exclusivos que o Android usa. Acho que você pode não estar satisfeito com a estrutura de código de teste subjacente em uso.

Você tentou testar áreas de código simuladas usando o Android Mock Framework?

Fred Grott
fonte
1
não tanto, testando de que a natureza não é tanto a questão como gravar o que está realmente acontecendo, enquanto o aplicativo é executado, o que eu realmente preciso é um vazamento de recursos de memória / Ferramenta de perfil
jottos