Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Boas Prática de Gerenciamento de Memória no And...

Boas Prática de Gerenciamento de Memória no Android

Esta apresentação explica um pouco de como o Android/Java gerencia memória, mostra alguns exemplos de memory leaks e mostra algumas dicas e boas práticas.

Avatar for Cristiano Madeira

Cristiano Madeira

March 21, 2015
Tweet

More Decks by Cristiano Madeira

Other Decks in Programming

Transcript

  1. OOM

  2. Como o android gerencia memória - Memória é um recurso

    valioso em dispositivos móveis - O Android não oferece área de swap para memória, ou seja toda paginação de memória mantêm-se apenas na memória RAM (Random Access Memory). Exceto alguns casos para arquivos estáticos. - O Android define um limite de tamanho de heap para cada app. Se o seu app atingir esse limite e tentar alocar mais algum objeto, você receberá um OOM (OutOfMemoryError). - O Android mantêm todos os apps recentemente abertos em um cache LRU (Least Recently Used). Quando sistema exige mais memória ele prioriza matar o processo do app menos utilizado. - O Android compartilha páginas de memória entre processos utilizando a política Copy On Write (COW). O processo pai "zygote" é iniciado no boot do sistema e carrega todo o framework do Android. A partir dai todo app que é iniciado faz um fork desse processo que que mantêm referencia ao framework. Assim todos os apps compartilham do mesmo recurso economizando memória. - O limite de memória para cada app varia entre 16/24/32/64/96/192 MB, de acordo com dispositivo.
  3. Heap x Stack Heap Stack - Toda instância de um

    objeto é alocada no heap. - O Garbage Collector age no heap. - As áreas de memória no heap possui acesso global pela aplicação. - A quantidade de áreas de memória disponíveis no heap é bem maior do que no stack. - É utilizado para alocar valores primitivos locais e referências para objetos de um método. - Todo objeto no heap possui uma referência no stack. - A memória é alocada no stack em forma de pilha (LIFO - Last In First Out). - A memória alocada no stack tem vida curta, enquanto no heap ela pode permanecer durante todo tempo de execução da aplicação. - O memória heap é alocada em uma estrutura de young generation e old generation.
  4. Heap x Stack public class Memory { public static void

    main(String[] args) { int i = 1; Object obj = new Object(); Memory mem = new Memory(); mem.foo(obj); } private void foo(Object param) { String str = param.toString(); System.out.println(str); } } Heap Memory http://www.journaldev.com/4098/java-heap-memory-vs-stack-memory-difference
  5. Garbage Collector - É responsável por liberar porções de memória

    que não estão mais mais utilizadas pela aplicação. Ou seja, o limpar porções de memória que não possuem mais nenhuma strong reference apontando para ela. - Utiliza o algoritmo mark-and-sweep para liberar as porções de memórias não utilizadas. - Não é totalmente a prova de memory leaks. - O GC realiza sua atividade por diversos motivos como: - A aplicação vai criar um novo objeto e não possui mais memória disponível. - Quando o heap estiver quase cheio. - Quando é chamado explicitamente através do método gc(); - Quando o GC limpa os objetos da young generation, esta limpeza é chamada de MINOR_GC e acontece mais com mais frequência. - Quando o GC limpa os objetos da old generation, esta limpeza é chamada e MAJOR_GC e acontece com menos frequência.
  6. Garbage Collector Fase 1: Mark Fase 2: Sweep O garbage

    collector começar pela raiz no stack e vai caminhando pela árvore de referência marcando todos os objetos alcançados. Todos os objetos que não foram marcados na primeira fase não são alcançáveis e são considerados lixo.
  7. Tipos de referências Weak Reference Strong Reference Phantom Reference Soft

    Reference - Objetos com esta referência serão sempre mantidos. - O mais utilizado. - Objetos com esta referência serão mantidos pelo GC enquanto houver memória disponível para o mesmo. - Estruturas simples de cache. - Objetos com esta referências serão mantidos até próximo ciclo do GC. - WeakHashMap - Objetos com esta referência, na verdade já nem existem mais e seu conteúdo não pode ser acessado. Ele já foi recolhido pelo GC, mas uma referência ao objeto destruído é mantida. - Raramente é usado.
  8. O que é um memory leak - Um memory leak

    acontece quando se perde o controle de uma strong reference para um objeto alocado, mantendo assim essa referência ativa e impedindo o garbage collector de reclamar essa porção de memória. O que reduz a quantidade de memória disponível para uso da aplicação e afeta a performance da aplicação e do sistema operacional.
  9. Inner class leak public class MainActivity extends ActionBarActivity { private

    static Leak leak = null;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 
 if (leak == null)
 leak = new Leak();
 
 leak.doTask();
 }
 
 public class Leak {
 public void doTask() {
 Toast.makeText(activity, "Task done!", Toast.LENGTH_SHORT).show();
 }
 } }
  10. Inner class leak public class MainActivity extends ActionBarActivity { private

    static Leak leak = null;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 
 if (leak == null)
 leak = new Leak();
 
 leak.doTask(this);
 }
 
 private static class Leak {
 public void doTask(MainActivity context) {
 MainActivity activity = new WeakReference<MainActivity>(context).get();
 Toast.makeText(activity, "Task done!", Toast.LENGTH_SHORT).show();
 }
 } }
  11. Anonymous inner class leak public class Singleton {
 
 public

    interface OnTaskDoneListener {
 void onTaskComplete();
 }
 
 private static Singleton mInstance;
 
 private ArrayList<OnTaskDoneListener> mQueue;
 
 public static Singleton getInstance() {
 if (mInstance == null) {
 mInstance = new Singleton();
 }
 
 return mInstance;
 }
 
 protected Singleton () {
 mQueue = new ArrayList<>();
 }
 
 public void addListener(OnTaskDoneListener listener) {
 mQueue(listener);
 }
 
 public void doTask() {
 // Task code
 
 for (OnTaskCompleteListener listener : mQueue) {
 if (listener != null)
 listener.onTaskComplete();
 } 
 }
 
 } public class AnonymousClassLeakActivity extends ActionBarActivity {
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 
 Singleton sgt = Singleton.getInstance();
 sgt.addListener(new Singleton .OnTaskCompleteListener() {
 @Override
 public void onTaskComplete() {
 Toast.makeText( AnonymousClassLeakActivity.this,
 "Task done!", Toast.LENGTH_SHORT).show();
 }
 });
 
 sgt.doTask();
 }
 
 }
  12. public class AnonymousClassLeakActivity extends ActionBarActivity {
 
 @Override
 protected void

    onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 
 Singleton sgt = Singleton.getInstance();
 sgt.addListener(new Singleton .OnTaskCompleteListener() {
 @Override
 public void onTaskComplete() {
 Toast.makeText( AnonymousClassLeakActivity.this,
 "Task done!", Toast.LENGTH_SHORT).show();
 }
 });
 
 sgt.doTask();
 }
 
 } public class Singleton {
 
 public interface OnTaskDoneListener {
 void onTaskComplete();
 }
 
 private static Singleton mInstance;
 
 private ArrayList<OnTaskDoneListener> mQueue;
 
 public static Singleton getInstance() {
 if (mInstance == null) {
 mInstance = new Singleton();
 }
 
 return mInstance;
 }
 
 protected Singleton () {
 mQueue = new ArrayList<>();
 }
 
 public void addListener(OnTaskDoneListener listener) {
 mQueue(listener);
 }
 
 public void doTask() {
 // Task code
 
 for (OnTaskCompleteListener listener : mQueue) {
 if (listener != null)
 listener.onTaskComplete();
 } mQueue.clear(); 
 }
 
 } Anonymous inner class leak
  13. public class ContextLeakActivity extends ActionBarActivity {
 
 @Override
 protected void

    onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 
 SingletonTwo sgt = SingletonTwo.getInstance();
 sgt.setContext(this);
 }
 
 } public class SingletonTwo {
 
 private static SingletonTwo mInstance;
 
 private Context mContext;
 
 public static SingletonTwo getInstance() {
 if (mInstance == null) {
 mInstance = new SingletonTwo();
 }
 
 return mInstance;
 }
 
 protected SingletonTwo() {}
 
 public void setContext(Context context) {
 if (mContext == null) {
 mContext = context;
 }
 }
 
 } Context leak
  14. public class ContextLeakActivity extends ActionBarActivity {
 
 @Override
 protected void

    onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 
 SingletonTwo sgt = SingletonTwo.getInstance();
 sgt.setContext(getApplicationContext());
 }
 
 } public class SingletonTwo {
 
 private static SingletonTwo mInstance;
 
 private Context mContext;
 
 public static SingletonTwo getInstance() {
 if (mInstance == null) {
 mInstance = new SingletonTwo();
 }
 
 return mInstance;
 }
 
 protected SingletonTwo() {}
 
 public void setContext(Context context) {
 if (mContext == null) {
 mContext = context;
 }
 }
 
 } Context leak
  15. Dicas para evitar memory leaks - Utilize classes internas estáticas,

    pois as classes internas não estáticas mantém uma referência para sua classe externa. - Use getApplicationContext() sempre que possível. Principalmente quando for passar o contexto para classes nas quais não se tem controle. Pois o contexto da aplicação é criado apenas uma vez e se mantêm vivo durante toda a execução da aplicação. - Otimize todos os arquivos de media (imagens, sons e vídeos) utilizados no seu app. - A maioria dos memory leaks em Android está ligado ao vazamento de contexto. Comece investigando as activities e fragments. - A maioria dos memory leaks está ligada a guardar objetos em variáveis estáticas. - Utilize weak reference quando for passar objeto que você não pode desalocar depois. - Sempre que registrar um listener, desregistre-o após o uso.
  16. Boas práticas - Utilize apenas bitmaps na resolução necessária para

    seu layout. - Não abuse de abstrações. Apesar de ser uma boa prática, o uso de abstrações aumentam significativamente o uso de memória.Utilize abstrações apenas se for trazer um benefício significante a sua aplicação. - Utilize com sabedoria as bibliotecas externas. Muitas delas não são desenvolvidas com dispositivos móveis em mente. - Faça uso consciente de frameworks de injeção dependência. Eles podem trazer um benefício significante para simplificar código, mais também executam vários processos de inicialização para análise de annotations que são mapeados na memória e podem manter essas porções memória ocupadas mesmo quando não estão sendo utilizados. - Acompanha de perto as referências. - Sempre acompanhe o uso de memória. - Tenha consciência do quanto está gastando de memória. - Enums utilizem o dobro de memória de uma constante estática. Evite utilizar enums - Toda classe utiliza por volta 500 bytes. - Cada instância criada utiliza entre 12-16 bytes
  17. Referências e Links - https://developer.android.com/training/articles/perf-tips.html - https://developer.android.com/training/articles/memory.html - https://developer.android.com/tools/debugging/debugging-memory.html -

    https://www.youtube.com/watch?v=_CruQY55HOk - http://www.cubrid.org/blog/dev-platform/understanding-java-garbage-collection/ - http://www.raizlabs.com/dev/2014/03/wrangling-dalvik-memory-management-in-android-part-1- of-2/5/ - http://www.raizlabs.com/dev/2014/04/hunting-your-leaks-memory-management-in-android- part-2-of-2/ - http://anatomyofandroid.com/2013/10/15/zygote/ - http://www.journaldev.com/4098/java-heap-memory-vs-stack-memory-difference - http://www.kdgregory.com/?page=java.refobj - http://blog.crowdint.com/2013/10/02/fixing-memory-leaks-in-android-applications.html