Java匿名對象導致的內存泄漏


這幾天與在某群與群友討論了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表達式代替。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM