@linux1s1s
2019-02-14T04:00:27.000000Z
字数 2817
阅读 3535
AndroidMemory 2015-04
考虑以下的代码:
/*** Example illustrating how threads persist across configuration* changes (which cause the underlying Activity instance to be* destroyed). The Activity context also leaks because the thread* is instantiated as an anonymous class, which holds an implicit* reference to the outer Activity instance, therefore preventing* it from being garbage collected.*/public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);exampleOne();}private void exampleOne() {new Thread() {@Overridepublic void run() {while (true) {SystemClock.sleep(1000);}}}.start();}}
当configuration改变的时候(典型的比如旋转屏幕)会导致Activity销毁和重建,你很容易想到,Android会清理内存,并且回收正在运行的线程和与之相关的内存。事实上,并非如此。反而是每当configuration改变一次,内存便会泄露一次,而由此引发的内存泄露会导致Android性能显著降低,在低配置Android手机甚至会出现ANR。
如果你读过Android 内存泄露-Handlers这篇文章,应该很容易发现内存泄露发生在哪里,显然Thread匿名内部类持有MainActivity的引用,而这个匿名内部类始终存活无论它的外部类是神马状态,而这个外部类恰恰是Activity,所以这个Activity的整个视图层次结构及其所有资源都将无法被GC回收。
为了验证上面的说法,重复多次旋转屏幕,此时通过 Eclipse Memory Analyzer(如果你读过Android内存分析(一)这篇文章)应该能得到类似下面这个图:

从上图可以看出,每次旋转屏幕都会泄露Activity的整个视图层次结构及其所有资源。
/*** This example avoids leaking an Activity context by declaring the* thread as a private static inner class, but the threads still* continue to run even across configuration changes. The DVM has a* reference to all running threads and whether or not these threads* are garbage collected has nothing to do with the Activity lifecycle.* Active threads will continue to run until the kernel destroys your* application's process.*/public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);exampleTwo();}private void exampleTwo() {new MyThread().start();}private static class MyThread extends Thread {@Overridepublic void run() {while (true) {SystemClock.sleep(1000);}}}}
此时线程不在持有外部类Activity的引用,所以每次旋转屏幕的时候GC很容易回收Activity的整个视图层次结构及其所有资源。
上面的优化其实并不推荐,由于Thread位于GC根部,DVM会和所有的活动线程保持hard references关系,所以运行中的Thread绝不会被GC无端回收了。所以,无论在任何情况下,你都必须对线程加入取消机制。就像下面这样:
/*** Same as example two, except for this time we have implemented a* cancellation policy for our thread, ensuring that it is never* leaked! onDestroy() is usually a good place to close your active* threads before exiting the Activity.*/public class MainActivity extends Activity {private MyThread mThread;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);exampleThree();}private void exampleThree() {mThread = new MyThread();mThread.start();}/*** Static inner classes don't hold implicit references to their* enclosing class, so the Activity instance won't be leaked across* configuration changes.*/private static class MyThread extends Thread {private boolean mRunning = false;@Overridepublic void run() {mRunning = true;while (mRunning) {SystemClock.sleep(1000);}}public void close() {mRunning = false;}}@Overrideprotected void onDestroy() {super.onDestroy();mThread.close();}}
上面的代码中,在Activity的onDestroy生命周期中关闭线程,已确保不会因为Thread引起内存泄露。
