In Android, Handler classes should be static or leaks might occur.
But where exactly is the leak and how might it happen? Let's determine the source of the problem by first documenting what we know:
When an Android application first starts, the framework creates a Looper object for the application's main thread. A Looper implements a simple message queue, processing Message objects in a loop one after another. All major application framework events (such as Activity lifecycle method calls, button clicks, etc.) are contained inside Message objects, which are added to the Looper's message queue and are processed one-by-one. The main thread's Looper exists throughout the application's lifecycle.
When a Handler is instantiated on the main thread, it is associated with the Looper's message queue. Messages posted to the message queue will hold a reference to the Handler so that the framework can call Handler#handleMessage(Message) when the Looper eventually processes the message.
In Java, non-static inner and anonymous classes hold an implicit reference to their outer class. Static inner classes, on the other hand, do not.
Consider the following code:
|
public class SampleActivity extends Activity {
private final Handler mLeakyHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
// ...
}
}
}
|
While not readily obvious, this code can cause cause a massive memory leak. Android Lint will give the following warning:
In Android, Handler classes should be static or leaks might occur.
But where exactly is the leak and how might it happen? Let's determine the source of the problem by first documenting what we know:
-
When an Android application first starts, the framework creates a
Looper
object for the application's main thread. ALooper
implements a simple message queue, processingMessage
objects in a loop one after another. All major application framework events (such as Activity lifecycle method calls, button clicks, etc.) are contained insideMessage
objects, which are added to theLooper
's message queue and are processed one-by-one. The main thread'sLooper
exists throughout the application's lifecycle. -
When a
Handler
is instantiated on the main thread, it is associated with theLooper
's message queue. Messages posted to the message queue will hold a reference to theHandler
so that the framework can callHandler#handleMessage(Message)
when theLooper
eventually processes the message. -
In Java, non-static inner and anonymous classes hold an implicit reference to their outer class. Static inner classes, on the other hand, do not.
So where exactly is the memory leak? It's very subtle, but consider the following code as an example:
|
public class SampleActivity extends Activity {
private final Handler mLeakyHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
// ...
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Post a message and delay its execution for 10 minutes.
mLeakyHandler.postDelayed(new Runnable() {
@Override
public void run() { }
}, 60 * 10 * 1000);
// Go back to the previous Activity.
finish();
}
}
|
When the activity is finished, the delayed message will continue to live in the main thread's message queue for 10 minutes before it is processed. The message holds a reference to the activity's Handler
, and the Handler
holds an implicit reference to its outer class (the SampleActivity
, in this case). This reference will persist until the message is processed, thus preventing the activity context from being garbage collected and leaking all of the application's resources. Note that the same is true with the anonymous Runnable class on line 15. Non-static instances of anonymous classes hold an implicit reference to their outer class, so the context will be leaked.
To fix the problem, subclass the Handler
in a new file or use a static inner class instead. Static inner classes do not hold an implicit reference to their outer class, so the activity will not be leaked. If you need to invoke the outer activity's methods from within the Handler
, have the Handler hold a WeakReference
to the activity so you don't accidentally leak a context. To fix the memory leak that occurs when we instantiate the anonymous Runnable class, we make the variable a static field of the class (since static instances of anonymous classes do not hold an implicit reference to their outer class):
|
public class SampleActivity extends Activity {
/**
* Instances of static inner classes do not hold an implicit
* reference to their outer class.
*/
private static class MyHandler extends Handler {
private final WeakReference<SampleActivity> mActivity;
public MyHandler(SampleActivity activity) {
mActivity = new WeakReference<SampleActivity>(activity);
}
@Override
public void handleMessage(Message msg) {
SampleActivity activity = mActivity.get();
if (activity != null) {
// ...
}
}
}
private final MyHandler mHandler = new MyHandler(this);
/**
* Instances of anonymous classes do not hold an implicit
* reference to their outer class when they are "static".
*/
private static final Runnable sRunnable = new Runnable() {
@Override
public void run() { }
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Post a message and delay its execution for 10 minutes.
mHandler.postDelayed(sRunnable, 60 * 10 * 1000);
// Go back to the previous Activity.
finish();
}
}
|
The difference between static and non-static inner classes is subtle, but is something every Android developer should understand. What's the bottom line? Avoid using non-static inner classes in an activity if instances of the inner class outlive the activity's lifecycle. Instead, prefer static inner classes and hold a weak reference to the activity inside.
As always, leave a comment if you have any questions and don't forget to +1 this blog in the top right corner! :)
相关推荐
标题《论文研究-A Tool for Testing Java Memory Leaks.pdf》和描述《一个测试Java内存泄漏的工具,党万春,钱巨,虽然Java语言有自带的垃圾回收机制,Java程序仍然存在由无效引用带来的内存泄漏。而内存泄漏会导致...
### Java内存泄漏详解 #### 摘要 本文旨在探讨如何在IBM WebSphere Application Server (WAS) 3.5、4.01 和 5.0 版本上诊断和定位 Java 内存泄漏问题,包括分布式环境和 z/OS 环境。文章将介绍一种通用的方法论,...
为了有效地排查和解决这类问题,开发者通常会借助专业工具,而“Java内存泄漏排除工具MAT”(Memory Analyzer Tool)就是一个强大的诊断工具。MAT是由Eclipse基金会开发的,用于分析Hprof格式的堆转储文件,帮助...
The Eclipse Memory Analyzer is a fast and feature-rich Java heap analyzer that helps you find memory leaks and reduce memory consumption. Use the Memory Analyzer to analyze productive heap dumps with...
有四种主要类型的内存问题:Performance问题,Resource constraints,Java heap leaks和Native memory leaks。Performance问题涉及过多的对象创建和垃圾收集延迟;Resource constraints通常与内存分配失败或页面交换...
The Eclipse Memory Analyzer is a fast and feature-rich Java heap analyzer that helps you find memory leaks and reduce memory consumption. Use the Memory Analyzer to analyze productive heap dumps with ...
JProfiler直觉式的GUI让你可以找到性能瓶颈、抓出内存漏失(memory leaks)、并解决执行绪的问题。它让你得以对heap walker作资源回收器的root analysis,可以轻易找出内存漏失;heap快照(snapshot)模式让未被参照...
JProfiler直觉式的GUI让你可以找到性能瓶颈、抓住内存泄漏 (memory leaks)、并解决多线程的问题。它让你得以对heap walker作资源回收器的root analysis,可以轻易找出内存泄漏;heap快照(snapshot)模式让未被引用...
Once an object is deemed unreachable, the garbage collector reclaims the associated memory, preventing memory leaks. 7. What is a Java package, and why is it used?Answer: A Java package is a ...
JProfiler是一个全功能的Java剖析工具(profiler),JProfiler可以找到性能瓶颈、抓住内存泄漏(memory leaks)、并解决多线程的问题。它让你得以对heap walker作资源回收器的root analysis,可以轻易找出内存泄漏;...
Eclipse Memory Analyzer(MAT)是一款强大的Java内存分析工具,它能帮助开发者诊断和解决与内存泄漏、内存占用过高以及垃圾收集性能问题相关的问题。在Java应用程序中,如果内存管理不当,可能会导致系统运行缓慢,...
JProfiler直觉式的GUI让你可以找到性能瓶颈、抓出内存漏失(memory leaks)、并解决执行绪的问题。它让你得以对heap walker作资源回收器的root analysis,可以轻易找出内存漏失;heap快照(snapshot)模式让未被参照...
IBM HeapAnalyzer is a graphical tool for discovering possible Java heap leaks. Steps Download: https://public.dhe.ibm.com/software/websphere/appserv/support/tools/HeapAnalyzer/ha457.jar Open a ...
JProfiler直觉式的GUI让你可以找到效能瓶颈、抓出内存漏失(memory leaks)、并解决执行绪的问题。它让你得以对heap walker作资源回收器的root analysis,可以轻易找出内存漏失;heap快照(snapshot)模式让未被参照...
JProfiler直觉式的GUI让你可以找到效能瓶颈、抓出内存漏失(memory leaks)、并解决执行绪的问题。它让你得以对heap walker作资源回收器的root analysis,可以轻易找出内存漏失;heap快照(snapshot)模式让未被参照...
JProfiler直觉式的GUI让你可以找到效能瓶颈、抓出内存漏失(memory leaks)、并解决执行绪的问题。它让你得以对heap walker作资源回收器的root analysis,可以轻易找出内存漏失;heap快照(snapshot)模式让未被参照...
JProfiler is an award-winning all-in-one Java profiler. JProfiler's intuitive GUI helps you find performance bottlenecks, pin down memory leaks and resolve threading issues
s.data[i] = nil // to avoid memory leaks. s.data = s.data[:i] return res } // Size returns the number of elements in the stack. func (s *Stack) Size() int { return len(s.data) } ``` 代码段通过一...
标题“Android-Memory-Leaks”直指这个问题的核心,即在Android应用程序中,特别是涉及到Fragment时,如何出现内存泄漏以及如何解决这些问题。Fragment是Android SDK中的一个关键组件,用于构建动态和模块化的用户...
JProfiler 直觉式的 GUI 让你可以找到效能瓶颈、抓出内存漏失 (memory leaks) 、并解决执行绪的问题。它让你得以对 heap walker 作资源回收器的 root analysis ,可以轻易找出内存溢出; heap 快照( snapshot )...