Quali strumenti e metodi Android funzionano meglio per trovare perdite di memoria / risorse? [chiuso]


152

Ho sviluppato un'app per Android, e sono in procinto di sviluppare un'app per telefono in cui tutto sembra funzionare bene e vuoi dichiarare la vittoria e spedire, ma sai che devono esserci solo perdite di memoria e risorse lì dentro; e c'è solo 16 MB di heap su Android ed è apparentemente sorprendentemente facile da perdere in un'app Android.

Mi sono guardato intorno e finora sono riuscito a trovare informazioni su 'hprof' e 'traceview' e nessuno dei due ha ricevuto recensioni favorevoli.

Quali strumenti o metodi hai incontrato o sviluppato e ti interessa condividere forse in un progetto OS?


3
Posso votare per far sì che Р̀СТȢѸ́ФХѾЦЧШЩЪЫЬѢѤЮѦѪѨѬѠѺѮѰѲѴ cambi il suo nome in qualcosa di leggibile dall'uomo.
JPM,

1
sarebbe opportuno riaprire questa domanda se rimuoviamo la parola "Strumenti Android" dal titolo della domanda? Le risposte trovate qui sono state molto utili per risolvere i miei problemi di perdita di memoria
k3b,

Ok, quindi ho un utente che passa continuamente da un'attività all'altra, il che significa che potrebbero aver cambiato 15 attività in 20 secondi. Potrebbe essere la causa di un errore di memoria insufficiente? Cosa devo fare per risolverlo? Grazie!
Ruchir Baronia,

1
Non posso fornire una risposta perché la domanda è stata chiusa, tuttavia ti consiglio di dare un'occhiata a Leak Canary . Usa la tua app, apri e chiudi le attività e lascia che la libreria faccia il suo lavoro. Ti dirà anche dove si è verificata la perdita. Dare all'analizzatore di perdite un po 'di tempo per fare il suo lavoro dopo che si è verificata la perdita - di solito ci vogliono circa 2 minuti o più per trovare la fonte della perdita. Dopodiché te lo presenterà ordinatamente in-app. Non sono necessari strumenti extra!
ubuntudroid,

Risposte:


90

Uno degli errori più comuni che ho riscontrato durante lo sviluppo di app Android è l'errore "java.lang.OutOfMemoryError: la dimensione bitmap supera il budget VM". Ho riscontrato questo errore in modo errato sulle attività che utilizzano molte bitmap dopo aver cambiato orientamento: l'attività viene distrutta, creata di nuovo e i layout vengono "gonfiati" dall'XML che consuma la memoria della VM disponibile per le bitmap.

Le bitmap sul layout di attività precedente non sono correttamente allocate dal garbage collector perché hanno incrociato i riferimenti alla loro attività. Dopo molti esperimenti ho trovato una soluzione abbastanza buona per questo problema.

Innanzitutto, imposta l'attributo "id" nella vista padre del 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"
     >
     ...

Quindi, sul metodo onDestroy () della tua attività, chiama il metodo unbindDrawables () passando un riferimento alla vista padre e quindi esegui un 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();
    }
}

Questo metodo unbindDrawables () esplora l'albero della vista in modo ricorsivo e:

  1. Rimuove i callback su tutti i drawable in background
  2. Rimuove i child su ogni viewgroup

3
Buona soluzione a un problema comune.
While-E

9
Questo non funziona per le sottoclassi di AdapterView (ListView, GridView ecc.).
Arjun,

@Arjun Sì..non funziona per le sottoclassi AdapterView. Per questo è necessario gestire l'eccezione. Resto funziona bene. Questo è quello che uso nel mio codice e funziona benissimo. Spero che questo ti aiuti.
hp.android,

@ hp.android Con "handle this in the exception" hai un esempio di come sarebbe? Lo chiedo perché ho pagine di immagini nella mia PageAdaptere mi sto lavorando su questo errore :(
Jacksonkr

4
@Jackson cambia semplicemente la condizione: se (visualizza l'istanza di ViewGroup &&! (Vedi l'istanza di AdapterView)) questo eliminerà l'eccezione che stai ottenendo per Adapter
hp.android


28

Principalmente per i viaggiatori di Google dal futuro:

La maggior parte degli strumenti Java non è purtroppo adatta a questo compito, poiché analizza solo JVM-Heap. Ogni applicazione Android ha anche un heap nativo, che deve anche rientrare nel limite di ~ 16 MB. Di solito viene utilizzato per i dati bitmap, ad esempio. Quindi puoi facilmente imbatterti in errori di memoria insufficiente anche se il tuo JVM-Heap si sta raffreddando intorno a 3 MB, se usi molti drawable.


6
l'avvio di drawable Android 3.0 (nido d'ape) viene archiviato nell'heap
Gu1234,

@Timo quindi cosa useresti per rilevare perdite nell'heap nativo?
Sydney

1
Test, molti test. Il problema è che non puoi nemmeno dire quanta memoria utilizza la tua app, a causa della condivisione della memoria e di altre tecniche di ottimizzazione. Puoi ottenere letture di memoria usando i soliti comandi di shell, ma quelli sono stime molto, molto approssimative.
Timo Ohr,

20

La risposta di @ hp.android funziona bene se stai solo lavorando con sfondi bitmap ma, nel mio caso, avevo BaseAdapterun set di ImageViews per a GridView. Ho modificato il unbindDrawables()metodo come consigliato in modo che la condizione sia:

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

ma il problema quindi è che il metodo ricorsivo non elabora mai i figli di AdapterView. Per affrontare questo, ho invece fatto quanto segue:

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();
}

in modo che i figli del AdapterViewsiano ancora elaborati - il metodo non tenta solo di rimuovere tutti i figli (che non è supportato).

Questo non risolve del tutto il problema, dal momento ImageViewche gestiscono una bitmap che non è il loro background. Ho quindi aggiunto quanto segue. Non è l'ideale ma funziona:

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

Complessivamente il unbindDrawables()metodo è quindi:

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();
  }
}

Spero che ci sia un approccio più di principio per liberare tali risorse.


12

Buona discussione su Google I / O (2011) sulla gestione della memoria in Android, nonché dettagli su strumenti + tecniche per la profilazione della memoria:
http://www.youtube.com/watch?v=_CruQY55HOk


4
O il post di blog corrispondente: android-developers.blogspot.com/2011/03/…
greg7gkb

1
Ok, quindi ho un utente che passa continuamente da un'attività all'altra, il che significa che potrebbero aver cambiato 15 attività in 20 secondi. Potrebbe essere la causa di un errore di memoria insufficiente? Cosa devo fare per risolverlo? Grazie!'
Ruchir Baronia,


1

Bene, quelli sono gli strumenti che si agganciano ai formati univoci che Android usa .. Penso che ciò di cui potresti non essere soddisfatto è il framework del codice di test sottostante in uso.

Hai provato a simulare test di aree di codice utilizzando Android Mock Framework?


1
non tanto, test di quella natura non è tanto il problema quanto la registrazione di ciò che sta realmente accadendo mentre l'applicazione è in esecuzione, ciò di cui ho davvero bisogno è uno strumento di profilazione delle risorse / memoria
jottos
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.