On Memory Leaks in Java and in Android.

Source: Internet
Author: User
Tags throwable

from:http://chaosinmotion.com/blog/?p=696



Just because it ' s a garbage collected language doesn ' t mean you can ' t leak memory or run out of it. Especially on Android where do I get so little to begin with.

Now's course sometimes the answer is so you just need more memory. If your program was a Java command line program to load the entire road map of the the same states to do some network Algorit HMS, you probably need more than the default JVM configurations give.

Sometimes it ' s not even a full-on leak, but a large chunk of memory isn ' t being released in time as a consequence of some Holder object that isn ' t being released in time.

There is some tools that can help. With Android, you can use the DDMS to get a idea what's going on, and you can even dump a snapshot of the heap by using the D UMP HPROF File option. (You can also programmatically capture uncaught exceptions on startup of your application or activity and dump an hprof fi Le within the exception handler like so:

public void OnCreate (Bundle savedinstancestate) {...    Thread.setdefaultuncaughtexceptionhandler (New Thread.uncaughtexceptionhandler ()    {        @Override        public void Uncaughtexception (thread thread, Throwable ex)        {            try {                file F = new File ( Environment.getexternalstoragedirectory (), "error.hprof");                String path = F.getabsolutepath ();                Debug.dumphprofdata (path);                LOG.D ("Error", "HREF dumped to" + path);            catch (IOException e) {                log.d ("Error", "Huh?")

", E);}} ); ...}

Of course once you have a. hprof file from the Android you had to convert it to something so can be used by an application such as Theeclipse Memory Analyzer tool using the HPROF-CONV command line application included as part of the Android SDK ; There is more information the-to-do-and-from use of the MAT tool here:attacking memory problems on Android.

One place where I ' ve been running into issues are with a clever little bit of code which loads images from a separate threa D from a remote resource, and puts them to a custom view that replaces the ImageView class. This little bit of code creates a background thread which are used to talk to a remote server to download images; Once the image is loaded, a callback causes the custom view to redraw itself with the correct contents. A snippet of that code is below:

/* Cache.java * * Created on May, from William Edward Woody */package com.chaosinmotion.android.utils;import java. Io. File;import Java.io.fileoutputstream;import Java.io.inputstream;import Java.util.hashset;import Java.util.linkedlist;import Java.util.map.entry;import Org.apache.http.httpentity;import Org.apache.http.httpresponse;import Org.apache.http.client.httpclient;import Org.apache.http.client.methods.httpget;import Org.apache.http.impl.client.defaulthttpclient;import Android.graphics.bitmap;import Android.graphics.bitmapfactory;import Android.os.handler;public class Cache{/** * O        UR callback interface */public interface callback {void loaded (String URL, Bitmap Bitmap);    void failure (String url, throwable th); }/** * Item in the queue which are waiting to being processed by our network thread (s) */private static CL        The queueitem {String URL;                Callback Callback; Queueitem (String u, Callback c)        {url = u;        callback = C;    }}///The handler to thread to the UI thread private handler Fhandler;    The event queue private linkedlist<queueitem> fqueue;    The global cache object, which'll is created by the class loader on load.    Because this was normally called from our UI objects, this means our Handler//would be created on our UI thread        public static Cache Gcache = new cache ();    /** * Internal runnable for our background loader thread */Private class Networkthread implements Runnable {public void Run () {//Start HTTP Client HttpClient HttpClient = new Defaulthttpclie                        NT (); for (;;)                {/* * Dequeue Next request */Queueitem Q;                        Synchronized (Fqueue) {while (Fqueue.isempty ()) {try {    Fqueue.wait ();                    } catch (Interruptedexception e) {} break; }/* Get the next item */q = f                Queue.removelast (); }/* * Read the network */t                    RY {/* * Set up the request and get the response */                    HttpGet get = new HttpGet (Q.url);                    HttpResponse response = Httpclient.execute (get);                                        httpentity entity = response.getentity (); /* Get the bitmap from the URL response */InputStream is = E                    Ntity.getcontent ();             Final Bitmap Bmap = Bitmapfactory.decodestream (IS);       Is.close ();                                        Entity.consumecontent ();                     /* * Send notification indicating we loaded the image on the * main UI thread                    */FINAL Queueitem QQ = q;                            Fhandler.post (New Runnable () {public void run () {                        Qq.callback.loaded (QQ.URL,BMAP);                }                    });                    } catch (Final Throwable ex) {final Queueitem QQ = q;                            Fhandler.post (New Runnable () {public void run () {                        Qq.callback.failure (QQ.URL,EX);                }                    });        }}//Httpclient.getconnectionmanager (). Shutdown ();    }}/** * Start up this object */Private Cache () {Fhandler = new Handler ();        Fqueue = new LinkedList ();        Thread th = new Thread (new Networkthread ());        Th.setdaemon (TRUE);    Th.start ();    }/** * Get the Singleton cache object */public static cache Get () {return gcache; }/** * Get the image from the remote service. This would call the callback once the * image has been loaded * @param URL * @param callback */Public V OID getImage (String URL, Callback Callback) {synchronized (fqueue) {Fqueue.addfirst (New Queueitem (Ur            L,callback));        Fqueue.notify (); }    }}

Now what this does is rather simple:we has a queue of items which be put into a linked list, and our background thread Loads those items, one at a time. Once the item is loaded, we are callback so the image can and is handled by whatever are using the service to load IM Ages from a network connection.

Of course we can make this far more sophisticated; We can save the loaded files to a cache, we can collapse multiple requests for the same image so we don ' t try to load it r epeatedly. We can also make the management of the threads more sophisticated by creating a thread group of multiple threads all Handl ing network loading.

We can then use this with a custom view class to draw the image, drawing a temporary image showing the real image hasn ' t b Een loaded yet:

/* Remoteimageview.java * * Created on the May, by William Edward Woody */package Com.chaosinmotion.android.utils;im Port Android.content.context;import Android.graphics.bitmap;import Android.graphics.canvas;import Android.graphics.color;import Android.graphics.paint;import Android.view.view;public class RemoteImageView extends    view{private Paint Fpaint;    Private Bitmap Fbitmap;    Private String FURL;        Public Remoteimageview (Context context) {super (context);        TODO auto-generated Constructor stub} public void Setimageurl (String url) {fbitmap = null;                FURL = URL;                Cache.get (). GetImage (FURL, New Cache.callback () {public void loaded (String URL, Bitmap Bitmap) {                Fbitmap = bitmap;            Invalidate (); } public void failure (String url, throwable th) {//ignoring for now. Could display broken link Image}});                } @Override protected void OnDraw (canvas canvas) {if (Fpaint = = null) Fpaint = new Paint ();        Canvas.drawcolor (Color.Black);        if (Fbitmap = = null) return;    Could display "not loaded" image Canvas.drawbitmap (fbitmap, 0, 0, fpaint); }}

This was a very simple example of our using the Cache object to the load images from a background thread. We can make this far more sophisticated; We can (for example) display a "Loading" image and a "image link broken" image. We can also alter the reported size during onmeasure to return the size of the bitmap, or we can center the displayed bitm AP or scale the bitmap to fit. But at it's core, we have a simple mechanism for displaying the loaded image on our system.

Can you spot the leak?

I didn ' t, at first.

Here ' s a hint:avoiding Memory Leaks

Here's another:the Remoteimageview, being a child of the View class, holds a reference to it's parent, and up the line UN Til we get to the top level activity, which holds a reference to–well–just about everything.

No?

Okay, here goes.

If we call:

        Cache.get (). GetImage (FURL, New Cache.callback () {...});

The anonymous inner class we create when we create our callback holds a reference to the Remoteimageview. And that inner class doesn ' t go away until after the image is loaded. If we have a few dozen of these and a very slow connection, the user switches from one activity to Another–and we can ' t Let the activity go, because we ' re still waiting for the images to load and being copied into the image view.

So while it's not exactly a memory leak, the class can ' t is let go of, nor can all the associated resources, until our con Nection completes or times out. In theory it's not a leak, exactly, because eventually the memory would be released–but it won ' t be released soon enough fo R our purposes. And so we crash.

So how does we fix this?

Well, we need to add the things. First, we need to somehow disassociate our view from the anonymous inner class so, when we view no longer exists, th E Callback class no longer holds a reference to the view. That, the activity can is reclaimed by the garbage collector even though our callback continues to exist. Second, we can remove the unprocessed callbacks so they do a network call to load an image which is no longer neede D.

To does the first, we change our anonymous inner class to a static class (that's the IT doesn ' t hold a virtual reference to ' t He '), and explicitly pass a pointer to our outer class to it, one of can then is removed:

/* Remoteimageview.java * * Created on the May, by William Edward Woody */package Com.chaosinmotion.android.utils;im Port Android.content.context;import Android.graphics.bitmap;import Android.graphics.canvas;import Android.graphics.color;import Android.graphics.paint;import Android.view.view;public class RemoteImageView extends    view{private Paint Fpaint;    Private Bitmap Fbitmap;    Private String FURL;        Private Ourcallback Fcallback;        Public Remoteimageview (Context context) {super (context);        TODO auto-generated Constructor stub} private static Class Ourcallback implements Cache.callback {                Private Remoteimageview pThis;        Ourcallback (Remoteimageview r) {pThis = R; public void loaded (String URL, Bitmap Bitmap) {if (pThis! = null) {PTh                Is.fbitmap = bitmap;                Pthis.invalidate (); Pthis.fcallback = null; Our CALLBACK ended; Remove reference}} public void failure (String url, throwable th) {//Ignorin G for now. Could Display broken link image if (pThis! = null) {pthis.fcallback = null;//Our callback End Ed        Remove Reference}}} public void Setimageurl (String url) {fbitmap = null;                FURL = URL;        Fcallback = new Ourcallback (this);    Cache.get (). GetImage (FURL, fcallback);                } @Override protected void OnDraw (canvas canvas) {if (Fpaint = = null) Fpaint = new Paint ();        Canvas.drawcolor (Color.Black);        if (Fbitmap = = null) return;    Could display "not loaded" image Canvas.drawbitmap (fbitmap, 0, 0, fpaint);  } @Override protected void Ondetachedfromwindow () {//Detach us from our callback if (fcallback! =                NULL) Fcallback.pthis = NULL;    Super.ondetachedfromwindow (); }}

The biggest changes is to create a new static Ourcallback class which holds a reference to the view being acted on. We then hold a reference to the callback that's zeroed out when the callback completes, either on failure or on success. Then on theOndetachedfromwindow callback, if we had a request outstanding (because Fcallback is not null), we de Tach the view from the callback. Note that because all the calls in the callback is done on the UI thread we don ' t need to synchronize access.

This would now detach the view from the callback when the view goes away and so the activity that contains the view can be rec Laimed by the memory manager.

Our second change are to remove the request from the queue, so we do not use unnecessary resources. While the strictly necessary for memory management purposes, it helps our network performance. The change here's to explicitly remove our callback from the queue.

First, we change our Ondetachedfromwindow () call-to-remove us (by callback) from the cache:

    @Override    protected void Ondetachedfromwindow ()    {        //Detach US callback        if (fcallback! = null) {            fcallback.pthis = null;            Cache.get (). Removecallback (Fcallback);        }                Super.ondetachedfromwindow ();    }

Second, we add a method to the cache to look for all instances of requests with the same callback, and delete the request From the queue. If it isn ' t in the queue, it's probably because the request is now being acted upon by our networking thread. (If We were particularly clever we could signal our networking thread to stop the network request, but I'm not going to do That's here.)

So we method added to the Cache are:

    /**     * Remove from the queue all requests with the specified callback. Done when the     * result was no longer needed because the view is going away.     * @param callback     *    /public void Removecallback (callback callback)    {        synchronized (fqueue) {            Iterator iter = Fqueue.iterator ();            while (Iter.hasnext ()) {                Queueitem i = Iter.next ();                if (I.callback = = callback) {                    iter.remove ();}}}}    

This iterates through the queue, removing entries that match the callback.

I ' ve noted this in my list of things not to forget because this (and variations of this) comes up, with holding references To Android View objects in a thread that can survive the destruction of an activity.

The basic model is while the view goes away (which we can detect with a callback to Ondetachedfromwindow), to disassociate The callback from the view and (preferably) to kill the background thread so the View object (and the activity associated With that view) can is garbage collected in a timely fashion.


On Memory Leaks in Java and in Android.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.