这几天与在某群与群友讨论了Runnable匿名对象导致内存泄漏的相关问题,特此记录一下。
示例代码如下:
package com.memleak.memleakdemo; public class Leaker { String valueToRead = "Hello world"; public void doSomething() { Thread bgThread = new Thread( new Runnable() { public void run() { while (true) { System.out.println("Running... ok"); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } ); bgThread.start(); } }
Main函数:
package com.memleak.memleakdemo; /** * Hello world! * */ public class App { public static void main( String[] args ) { Leaker l = new Leaker(); l.doSomething(); } }
问题出在哪?
启动此程序,main函数对应的线程在调用Leaker之后,应该退出了,后台只有一个Runnable在执行,理论上此时Leaker对象没有任何东西引用,此时应该被GC才对,但是如果使用visualVM查看下内存:
即使强制GC之后,此对象依旧存在,说明发生了泄露。
在上面图中的例子使用了一个匿名的Runnable对象,如果将此Runnable改为一个显式声明的对象,如下例子所示:
package com.memleak.memleakdemo; public class CauseLeakerNotToLeak implements Runnable { public void run() { while (true) { System.out.println("Running... ok"); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
LeakerSolved.java
package com.memleak.memleakdemo; public class LeakerSolved { String valueToRead = "Hello world"; public void doSomething() { Thread bgThread = new Thread( new CauseLeakerNotToLeak() ); bgThread.start(); } }
通过VisualVM则会发现已经不再泄露了:
当然,如果使用Java 8带的Lambda表达式:
package com.memleak.memleakdemo; public class LeakerLambda { String valueToRead = "Hello world"; public void doSomething() { Thread bgThread = new Thread(() -> {while(true) { System.out.println("Running... ok"); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } }}); bgThread.start(); } }
也能解决这个问题:
结论:
在创建线程的时候一定要谨慎使用匿名Runnable对象,最好使用命名对象或者Lambda表达式代替。
原文:https://www.cnblogs.com/nullifier/p/12356935.html