On Memory Leaks in Java and in Android.
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 you get so little to begin with.
Now of course sometimes the answer is that you just need more memory. If your program is a Java command line program to load the entire road map of the United States to do some network algorithms, you probably need more than the default JVM configurations
give you.
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 are some tools that can help. With Android, you can use DDMS to get an idea what’s going on, and you can even dump a snapshot of the heap by using the Dump HPROF File option. (You can also programmatically capture uncaught exceptions on startup of your application or activity and dump an hprof file 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 an .hprof file from Android you have to convert it to something that can be used by an application such as the
Eclipse Memory Analyzer tool using the hprof-conv command line application included as part of the Android SDK; there is more information on how to do this and how to use the MAT tool here:
Attacking memory problems on Android.

One place where I’ve been running into issues is with a clever little bit of code which loads images from a separate thread from a remote resource, and puts them into a custom view that replaces the ImageView class. This little bit of code creates a background
thread which is 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 15, 2011 by 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
{
/**
* Our callback interface
*/
public interface Callback
{
void loaded(String url, Bitmap bitmap);
void failure(String url, Throwable th);
} /**
* Item in the queue which is waiting to be processed by our network thread(s)
*/
private static class 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 will be created by the class loader on load.
/// Because this is normally called from our UI objects, this means our Handler
/// will 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 DefaultHttpClient(); for (;;) {
/*
* Dequeue next request
*/
QueueItem q; synchronized(fQueue) {
while (fQueue.isEmpty()) {
try {
fQueue.wait();
}
catch (InterruptedException e) {
}
break;
} /*
* Get the next item
*/
q = fQueue.removeLast();
} /*
* Read the network
*/ try {
/*
* 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 = entity.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 will call the callback once the
* image has been loaded
* @param url
* @param callback
*/
public void getImage(String url, Callback callback)
{
synchronized(fQueue) {
fQueue.addFirst(new QueueItem(url,callback));
fQueue.notify();
}
}
}
Now what this does is rather simple: we have a queue of items which are put into a linked list, and our background thread loads those items, one at a time. Once the item is loaded, we call our callback so the image can then be handled by whatever is using
the service to load images 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 repeatedly. We can also make the management of the threads more sophisticated by creating
a thread group of multiple threads all handling 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 been loaded yet:
/* RemoteImageView.java
*
* Created on May 15, 2011 by William Edward Woody
*/ package com.chaosinmotion.android.utils; import 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 is a very simple example of our using the Cache object to 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 bitmap or scale the bitmap to fit. But at it’s core, we have a simple mechanism for displaying the loaded image in 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 until we get to the top level activity, which holds a reference to–well–just about everything.

No?
Okay, here goes.
So when 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. So 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 be copied into the image view.
So while it’s not exactly a memory leak, the class can’t be let go of, nor can all the associated resources, until our connection completes or times out. In theory it’s not a leak, exactly, because eventually the memory will be released–but it won’t be released
soon enough for our purposes. And so we crash.

So how do we fix this?
Well, we need to add two things. First, we need to somehow disassociate our view from the anonymous inner class so that, when our view no longer exists, the callback class no longer holds a reference to the view. That way, the activity can be reclaimed by
the garbage collector even though our callback continues to exist. Second, we can remove the unprocessed callbacks so they don’t make a network call to load an image that is no longer needed.
To do the first, we change our anonymous inner class to a static class (that way it doesn’t hold a virtual reference to ‘this’), and explicitly pass a pointer to our outer class to it, one that can then be removed:
/* RemoteImageView.java
*
* Created on May 15, 2011 by William Edward Woody
*/ package com.chaosinmotion.android.utils; import 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) {
pThis.fBitmap = bitmap;
pThis.invalidate();
pThis.fCallback = null; // our callback ended; remove reference
}
} public void failure(String url, Throwable th)
{
// Ignoring for now. Could display broken link image
if (pThis != null) {
pThis.fCallback = null; // our callback ended; 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 two 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 is zeroed out when the callback completes, either on failure or on success. Then on the
onDetachedFromWindow callback, if we have a request outstanding (because fCallback is not null), we detach the view from the callback. Note that because all the calls in the callback are done on the UI thread we don’t need to synchronize access.
This will now detach the view from the callback when the view goes away, so the activity that contains the view can be reclaimed by the memory manager.
Our second change is to remove the request from the queue, so we don’t use unnecessary resources. While not strictly necessary for memory management purposes, it helps our network performance. The change here is 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 from our 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 here.)
So our method added to the Cache is:
/**
* Remove from the queue all requests with the specified callback. Done when the
* result is 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 on 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 when 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 be garbage collected in a timely fashion.
On Memory Leaks in Java and in Android.的更多相关文章
- Find out when memory leaks are a concern and how to prevent them
Handling memory leaks in Java programs Find out when memory leaks are a concern and how to prevent t ...
- Android性能优化之被忽视的Memory Leaks
起因 写博客就像讲故事.得有起因,经过,结果,人物.地点和时间.今天就容我给大家讲一个故事. 人物呢.肯定是我了. 故事则发生在近期的这两天,地点在coder君上班的公司.那天无意中我发现了一个奇怪的 ...
- The Introduction of Java Memory Leaks
One of the most significant advantages of Java is its memory management. You simply create objects a ...
- Diagnosing out of memory errors and memory leaks 内存泄露实例 C Java JavaScript 内存泄露
小结: 1. 数据库连接池. JDBC语句和结果对象必须显式地关闭. 2. 电梯到目标楼层后地址是否被释放 When a button is pressed: Get some memory, whi ...
- Debugging Java Native Memory Leaks
GZIP造成JAVA Native Memory泄漏案例 https://www.elastic.co/blog/tracking-down-native-memory-leaks-in-elasti ...
- Activitys, Threads, & Memory Leaks
Activitys, Threads, & Memory Leaks 在Android编程中,一个公认的难题是在Activity的生命周期如何协调长期运行的任务和避免有可能出现的内存泄漏问题. ...
- 内存泄漏 Memory Leaks 内存优化 MD
Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...
- [转]Activitys, Threads, & Memory Leaks
转自:http://www.androiddesignpatterns.com/2013/04/activitys-threads-memory-leaks.html http://www.cnblo ...
- 内存泄露 Memory Leaks
什么是内存泄露 内存管理一直是Java 所鼓吹的强大优点.开发者只需要简单地创建对象,而Java的垃圾收集器将会自动管理内存空间的分配和释放. 但在很多情况下,事情并不那么简单,在 Java程序中总是 ...
随机推荐
- Map/Reduce之间的Partitioner接口
一.Partitioner介绍 Partitioner的作用是对Mapper产生的中间结果进行分片,以便将同一分组的数据交给同一个Reduce处理,它直接影响Reduce阶段的负载均衡(个人理解:就是 ...
- iframe 透明
iframe是迫不得已才使用的,因为使用iframe会带来较多的问题,而有的浏览器可以设置将iframe当作广告屏蔽. 在最近的一个工作内容中使用了iframe,开始遇到的问题是iframe高度自适应 ...
- 我的Myeclipse黑色主题
- sql语句中获取datetime任何部分
sql语句中获取datetime的日期部分 sql语句中 经常操作操作datetime类型数据.今天在写一个存储过程的时候需要将 一个datetime的值的 日期部分提取出来.网上有许多这方面的介绍. ...
- 我喜欢的乐队-Euphoria
来自日本的后摇乐团,001年冬天由森川裕之.佐藤昭太.木下阳辅三人于东京组建,2003年签约日本独立厂牌123Record,并发行首张EP细碟<Floral Dew>.包括EP.Singl ...
- 【和我一起学python吧】初学Python,版本如何选择?
早在四年多以前,在我进入英才网之前,去面试过一家海归创业的公司.他们需要的是有unix开发经验的技术人员,但是因为他们当时所处的阶段对很多成熟 技术人员不是很吸引,所以条件放宽为熟悉面向对象的程序开发 ...
- Chrome多线程模型
为什么使用多线程? Chrome的多线程模型主要解决什么问题? 如何实现该问题的解决? 1. 解决问题 Chrome有很多线程,这是为了保持UI线程(主线程)的高响应度,防止被其他费时的操作阻碍从而影 ...
- Hbase笔记——RowKey设计
一).什么情况下使用Hbase 1)传统数据库无法承载高速插入.大量读取. 2)Hbase适合海量,但同时也是简单的操作. 3)成熟的数据分析主题,查询模式确立不轻易改变. 二).现实场景 1.电商浏 ...
- 使用logback.xml配置来实现日志文件输出
转自:http://sungang-1120.iteye.com/blog/2104296 Logback是由log4j创始人设计的又一个开源日志组件.logback当前分成三个模块:logback- ...
- 第二百六十五天 how can I 坚持
每天上班闲着没事干好蛋疼啊.周报都不知道怎么写了. 今天加上了毕梦琪,哎,不知咋聊.好烦. 要搞angelarjs了,希望有机会,多搞点东西,要当架构师,哈哈. 量子计算,什么高深的东西,百度百科了下 ...