2012-11-23 26 views
12

我想我的Android動態壁紙有內存泄漏。每當我旋轉屏幕時,收集的內存垃圾量增加50kb,並且不會回落。我認爲這可能是由於預定的未來造成的,所以我將介紹一個場景以查看是否屬於這種情況。預定的未來會導致內存泄漏嗎?

假設你有一個類(姑且稱之爲美孚),有以下成員。

private ScheduledFuture<?> future; 
private final ScheduledExecutorService scheduler = Executors 
     .newSingleThreadScheduledExecutor(); 

private final Runnable runnable = new Runnable() { 
    public void run() { 
     // Do stuff 
    } 
}; 

現在你設定一個計劃未來

future = scheduler.scheduleAtFixedRate(runnable, delay, speed, 
       TimeUnit.MILLISECONDS); 

未來持有的可運行的引用,可運行保持父Foo對象的引用。我不確定情況是否如此,但是這意味着如果程序中沒有任何內容引用Foo,垃圾收集器仍然無法收集它,因爲有一個預定的未來?我並不擅長多線程,所以我不知道我所展示的代碼是否意味着計劃任務的壽命會長於對象,這意味着它不會最終被垃圾收集。

如果這種情況下不會導致富防止被垃圾收集,我只是需要被告知,有一個簡單的解釋。如果它確實阻止Foo被垃圾收集,那麼我該如何解決它?必須做future.cancel(true); future = null;future = null部分是不必要的?

回答

5
  • 您的run方法依賴封閉的Foo類,因此不能獨立生活。在這種情況下,我不知道如何讓你的Foo gc'ed,並保持你的可運行的「活着」由執行者
  • 運行或您的run方法是靜態的,因爲它不依賴於狀態你的Foo類,在這種情況下,你可以使其靜態,它會防止你遇到的問題。

你似乎並不在你的Runnable來處理中斷。這意味着,即使您致電future.cancel(true),您的Runnable仍會繼續運行,您認爲這可能是導致泄漏的原因。

有幾種方法可以讓一個Runnable「中斷型」。要麼調用引發InterruptedException的方法(如Thread.sleep()或阻塞IO方法),並且在未來取消時它將拋出InterruptedException。您可以捕獲該異常,並已經清理了需要清理和恢復被中斷的狀態是什麼後及時退出run方法:

public void run() { 
    while(true) { 
     try { 
      someOperationThatCanBeInterrupted(); 
     } catch (InterruptedException e) { 
      cleanup(); //close files, network connections etc. 
      Thread.currentThread().interrupt(); //restore interrupted status 
     } 
    } 
}  

如果不調用任何這樣的方法,標準的成語是:

public void run() { 
    while(!Thread.currentThread().isInterrupted()) { 
     doYourStuff(); 
    } 
    cleanup(); 
} 

在這種情況下,您應該嘗試確保定期檢查while中的條件。

通過這些更改,當您調用future.cancel(true)時,將向執行Runnable的線程發送一箇中斷信號,該線程將退出正在執行的任務,從而使您的Runnable和您的Foo實例符合GC要求。

+0

我的run方法不依賴於封閉Foo類。但是,在我銷燬對Foo對象的引用之前,我需要做些什麼來阻止可運行引發內存泄漏。 'future.cancel(真)'是否足夠?我已經這樣做了,內存泄漏仍然存在。當然,這可能意味着runnable不是泄漏的來源。 – gsingh2011

+0

@ gsingh2011你確定'future.cancel(true)'取消了你的未來嗎?換句話說,你的runnable可以被中斷嗎?當它中斷時它會終止它的工作嗎? – assylias

+0

因爲我不明白「你的runnable可以被中斷的意思是」我認爲它不能被打斷。我應該怎麼做? – gsingh2011

0

未來保持到可運行的引用,並且可運行保持 參考父Foo對象。我不知道,如果是這樣的話, 但可能這一事實意味着,如果沒有在程序中持有 參考美孚,垃圾收集器仍無法收集 因爲有一個計劃未來?

它是一個糟糕的主意,使Foo某種瞬態對象,你經常創造,因爲你應該關閉ScheduledExecutorService scheduler當你的應用程序關閉。因此你應該讓Foo成爲一個僞單身。

垃圾收集明白週期如此,一旦你關閉Foo的執行服務,您將很可能不會有內存問題。

5

雖然這個問題是早就回了回答,但閱讀this文章後,我想用張貼新的解釋回答。

排定未來可能會導致內存泄漏? --- YES

ScheduledFuture.cancel()Future.cancel()一般不會通知其Executor,它已被取消,它停留在隊列中,直到它的執行時間已經到來。對於簡單的期貨來說這不是什麼大問題,但對於ScheduledFutures來說可能是一個大問題。它可以在那裏停留幾秒鐘,幾分鐘,幾小時,幾天,幾周,幾年或幾乎無限期地取決於它的計劃延遲。

這裏是最糟糕的情況的例子。即使其Future已被取消,runnable及其引用的所有內容仍將保留在Long.MAX_VALUE毫秒的隊列中!

public static void main(String[] args) { 
    ScheduledThreadPoolExecutor executor 
     = new ScheduledThreadPoolExecutor(1); 

    Runnable task = new Runnable() { 
     @Override 
     public void run() { 
      System.out.println("Hello World!"); 
     } 
    }; 

    ScheduledFuture future 
     = executor.schedule(task, 
      Long.MAX_VALUE, TimeUnit.MILLISECONDS); 

    future.cancel(true); 
} 

您可以通過使用Profiler或致電ScheduledThreadPoolExecutor.shutdownNow()方法,它會返回一個列表,在它(它是被取消了了Runnable)一個元素看到這一點。

這個問題的解決方法是寫自己的未來實現或飄飛調用purge()方法。在自定義執行人工廠解決方案案例是:

public static ScheduledThreadPoolExecutor createSingleScheduledExecutor() { 
    final ScheduledThreadPoolExecutor executor 
     = new ScheduledThreadPoolExecutor(1); 

    Runnable task = new Runnable() { 
     @Override 
     public void run() { 
      executor.purge(); 
     } 
    }; 

    executor.scheduleWithFixedDelay(task, 30L, 30L, TimeUnit.SECONDS); 

    return executor; 
} 
+1

您能解釋一下,在這種情況下會發生什麼?這是一個單線程執行程序。 如果Runnable任務是長時間運行的任務(可以說是一天),延遲和週期與上面相同。任務是否排隊?任務隊列上限是否有限制? – cherryhitech