English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Für Memory Leaks ist es in Android relativ einfach, wenn man nicht aufpasst, sie zu erstellen, besonders in Activities, sie treten relativ häufig auf. Lassen Sie mich Ihnen sagen, wie ich Memory Leaks finde.
Was ist ein Memory Leak?
Memory Leaks sind solche Objekte, die in der Speicher nicht mehr verwendet werden, aber die Müllentsorgungsmaschine kann sie nicht recyceln, was bedeutet, dass sie im Speicher verbleiben und den Speicherverbrauch immer weiter erhöhen, was letztlich zu einer schlechteren Programmausführung führt.
Im Android-Virtualmaschinen wird die Suchalgorithmen für Wurzelknoten verwendet, um Wurzelknoten zu enumerieren und zu bestimmen, ob sie Müll sind. Der Virtualmaschinen beginnt mit GC Roots zu durchsuchen. Wenn ein Knoten keine Route zum GC Roots finden kann, das heißt, er ist nicht mit GC Roots verbunden, dann bedeutet das, dass die Referenz ungültig ist und recycelt werden kann. Memory Leaks sind die schlechten Aufrufe, die einige nutzlose Objekte und GC Roots verbinden, die nicht recycelt werden können.
Da wir wissen, was ein Memory Leak ist, wissen wir natürlich auch, wie man es vermeidet, das ist, dass wir bei der Code-Schreibung darauf achten, dass wir lange Zeit Referenzen auf nutzlose Objekte haben, was einfach klingt, aber ausreichend Erfahrung erfordert, um es zu erreichen, daher treten Memory Leaks relativ häufig auf. Da sie nicht vollständig vermieden werden können, müssen wir in der Lage sein, Memory Leaks in unserem Programm zu erkennen und zu beheben.
Lassen Sie mich Ihnen sagen, wie man Memory Leaks entdeckt.
Suchen Sie nach Speicherleck:
Zum Beispiel das folgende Code:
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); String string = new String(); } public void click(View view){ Intent intent = new Intent(); intent.setClass(getApplicationContext(), SecondActivity.class); startActivity(intent); } }
public class SecondActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_second); Runnable runnable = new Runnable() { @Override public void run() { try { Thread.sleep(8000000L); catch (InterruptedException e) { e.printStackTrace(); } } }; new Thread(runnable).start(); } }
Jedes Mal, wenn auf diese Activity umgesprungen wird, wird ein Thread aufgerufen, der dann die run-Methode von runnable ausführt, da Runnable ein anonymes internes Objekt ist, besitzt es eine Referenz auf SecondActivity, daher können zwei einfache Activities, die von MainActivity aus auf SecondActivity umgesprungen werden können, einfach miteinander umspringen. Wir springen also von MainActivity zu SecondActivity und dann von SecondActivity zurück zu MainActivity, und das geschieht so fort.5mal, kehrt schließlich MainActivity zurück. Nach der Logik sollten SecondActivity nach dem Rückkehr zu MainActivity zerstört und recycelt werden, aber das könnte tatsächlich nicht so sein.
Um zu beurteilen, ob es zu einem Speicherüberschuss gekommen ist, müssen Sie Werkzeuge verwenden! Es gibt zwei Arten
1.Verwenden Sie das MAT-Tool, um zu suchen
Öffnen Sie zunächst das Android Device Monitor-Tool in AS, wie im folgenden Bild gezeigt:
Öffnen Sie es und Sie sehen das folgende Interface
Wählen Sie zunächst den Pakznamen der Anwendung aus, die Sie überprüfen möchten, und klicken Sie dann auf den markierten Bereich im folgenden Bild. Ein Symbol wird nach dem Pakznamen der Anwendung markiert
Das zu tun ist, unseren app zu operieren, um hin und her zu springen5mal.
Dann klicken Sie auf das Symbol im folgenden Bild, um die hprof-Datei zum Analysieren auszugeben
Das exportierte Datei ist wie im folgenden Bild gezeigt:
Nachdem wir die hprof-Datei erhalten haben, können wir das MAT-Tool verwenden, um zu analysieren
Öffnen Sie das MAT-Tool
Wenn nicht, können Sie die folgenden Website herunterladen
MAT-Tool-Download-Adresse
Das Interface ist wie im folgenden Bild gezeigt:
Öffnen Sie die zuvor exportierte hprof-Datei und es wird möglicherweise der folgende Fehler angezeigt
Dies liegt daran, dass MAT verwendet wird, um hprof-Dateien von Java-Programmen zu analysieren und mit den von Android exportierten hprof-Dateien eine bestimmte Formatunterschiede aufweist, daher müssen wir die exportierten hprof-Dateien konvertieren. Das SDK bietet uns das Konvertierungstool hprof-conv.exe befindet sich im folgenden Bild
Dann navigieren wir zu diesem Pfad und führen diesen Befehl aus, um unsere hprof-Datei zu konvertieren, wie im folgenden Bild gezeigt
d.h. hprof-So verwenden Sie das conv-Befehl
hprof-conv Quelldatei Ausgabedatei
z.B. hprof-conv E:\aaa.hprof E:\output.hprof
d.h. aaa.hprof in output.hprof umwandeln und ausgeben. output.hprof ist die Datei, die wir konvertiert haben, wie im Bild gezeigt mat2.hprof ist die Datei, die wir konvertiert haben.
Nächste, verwenden wir das MAT-Tool, um die konvertierte mat2.hprof-Datei, öffnen Sie sie und es wird kein Fehler angezeigt, wie im folgenden Bild gezeigt:
Dann können wir die im aktuellen Speicher vorhandenen Objekte ansehen. Da内存泄漏通常发生在Activity中,因此我们只需要查找Activity。
Klicken Sie auf das markierte QQL-Symbol im folgenden Bild und geben Sie select ein * from instanceof android.app.Activity
ähnlich einem SQL-Befehl zur Suche nach Informationen über Activity, klicken Sie auf das rote Ausrufezeichen ausführen, wie im folgenden Bild gezeigt:
接下来我们就可以看到下面过滤到的Activity信息了
如上图所示,其中内存中还存在 6个SecondActivity实例,但是我们是想要全部退出的,这表明出现了内存泄漏
其中 有 Shallow size 和 Retained Size两个属性
Shallow Size 对象自身占用的内存大小,不包括它引用的对象。针对非数组类型的对象,它的大小就是对象与它所有的成员变量大小的总和。 当然这里面还会包括一些java语言特性的数据存储单元。针对数组类型的对象,它的大小是数组元素对象的大小总和。 Retained Size Retained Size=当前对象大小+当前对象可直接或间接引用到的对象的大小总和。(间接引用的含义:A->B->C, C就是间接引用) 不过,释放的时候还要排除被GC Roots直接或间接引用的对象。他们暂时不会被当做Garbage。
接下来右击一个SecondActivity
选择with all references
打开如下所示的页面
查看下图的页面
看到this0引用了这个Activity而this0表示内部类的意思,也就是一个内部类引用了Activity,而this$0又被target引用,target是一个线程,原因找到了,内存泄漏的原因就是Activity被内部类引用,而内部类又被线程使用,因此无法释放,我们转到这个类的代码处查看
public class SecondActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_second); Runnable runnable = new Runnable() { @Override public void run() { try { Thread.sleep(8000000L); catch (InterruptedException e) { e.printStackTrace(); } } }; new Thread(runnable).start(); } } Tatsächlich in
Tatsächlich gibt es in SecondActivity einen Runnable-Internklassenobjekt, das dann von dem Thread verwendet wird, und der Thread muss ausgeführt werden8000 Sekunden, daher wird das Objekt SecondActivity referenziert und kann nicht freigegeben werden, was zu einem Speicherüberlauf führt.
Um dieses Arten von Speicherüberlauf zu lösen, müssen Sie sicherstellen, dass der Thread beim Verlassen der Activity beendet wird (obwohl das nicht so gut funktioniert ..), oder die Ausführungszeit der Threads gut kontrollieren.
So haben wir den Speicherüberlauf in diesem Programm gefunden.
2.Finden Sie den Speicherüberlauf direkt mit Monitor Memory von Android Studio
Noch einmal mit demselben Programm, ich werde es einfacher sagen.
Zunächst führen Sie das Programm auf dem Telefon aus und öffnen Sie die Monitor-Schnittstelle von AS, um das Memory-Bild anzuzeigen
Klicken Sie auf das Symbol des kleinen Lastwagens (in1Der Positionssymbol) kann eine GC auslösen
Klicken Sie auf2Der Positionssymbol kann die hprof-Datei anzeigen
Links sind die Objekte im Speicher, darin suchen wir nach der Activity, ob sie existiert, die wir hoffen, bereits freigegeben zu haben. Wenn die Activity, die wir hoffen, bereits freigegeben zu haben, auftaucht, wird durch einen Klick die Gesamtzahl auf der rechten Seite angezeigt. Durch Klicken auf eines der Elemente auf der rechten Seite kann das Baumdiagramm der GC Roots angezeigt werden. Durch das Betrachten des Diagramms können Sie den Ort des Memleaks finden (ähnlich wie im ersten Ansatz).
So haben wir die Memleaks gefunden.
Die Ursachen der Memleaks in Android werden in etwa in die folgenden Kategorien eingeteilt:
1.Memleak durch statische Variablen verursacht
Da der Lebenszyklus von statischen Variablen von der Zeit der Klassenladung bis zur Klassenentsorgung beginnt und endet, das heißt, statische Variablen werden erst dann freigegeben, wenn der Prozess des Programms stirbt, und wenn in der statischen Variable die Activity referenziert wird, dann wird diese Activity aufgrund der Referenz mit dem Lebenszyklus der statischen Variablen identisch sein und daher nicht freigegeben werden können, was zu einem Memleak führt.
解决办法:
Verwenden Sie getApplicationContext, wenn die Activity durch eine statische Variable referenziert wird, da der Lebenszyklus der Application von der Startzeit des Programms bis zu seinem Ende, wie der von statischen Variablen, ist.
2.Memleak durch Thread verursacht
In ähnlicher Weise wie im obigen Beispiel, ist die Ausführungszeit der Thread sehr lang, und selbst wenn die Activity ausgelöst wird, wird sie weiterhin ausgeführt, da der Thread oder der Runnable eine interne Klasse der Activity ist, daher besitzt er eine Instanz der Activity (da die Erstellung von internen Klassen von der externen Klasse abhängt), was dazu führt, dass die Activity nicht freigegeben werden kann.
AsyncTask有线程池,问题更严重
解决办法:
1合理安排线程执行的时间,控制线程在Activity结束前结束。
2将内部类改为静态内部类,并使用弱引用WeakReference来保存Activity实例,因为弱引用只要GC发现了就会回收它,因此可尽快回收
3BitMap占用过多内存
bitmap的解析需要占用内存,但是内存只提供8M的空间给BitMap,如果图片过多,并且没有及时recycle bitmap那么就会造成内存溢出。
解决办法:
及时recycle压缩图片之后加载图片
4资源未被及时关闭造成的内存泄漏
比如一些Cursor没有及时close会保存有Activity的引用,导致内存泄漏
解决办法:
在onDestroy方法中及时close即可
5Handler的使用造成的内存泄漏
由于在Handler的使用中,handler会发送message对象到MessageQueue中,然后Looper会轮询MessageQueue,然后取出Message执行,但是如果一个Message长时间没有被取出执行,那么由于Message中有Handler的引用,而Handler一般来说也是内部类对象,Message引用Handler,Handler引用Activity,这样使得Activity无法回收。
解决办法:
依旧使用静态内部类+弱引用的方式可解决
其中还有一些关于集合对象未移除,注册的对象未反注册,代码压力的问题也可能产生内存泄漏,但是使用上述的几种解决办法一般都可以解决的。