2013-04-25 29 views
5

我的一個應用程序積累了很多Thread GC無法拾取和清除的實例。這種內存泄漏從長遠來看會使應用程序崩潰。爲什麼我的線程不會死機並導致內存泄漏?

我不是100%肯定是哪裏來的,但我有一個明顯的感覺,下面威力是有問題的代碼:

public class UraHostHttpConnection extends AbstractUraHostConnection { 
    private Handler uiThreadHandler = new Handler(Looper.getMainLooper()); 
    private Executor taskExecutor = new Executor() { 
     public void execute(Runnable command) { 
      new Thread(command).start(); 
     } 
    }; 
    private ConnectionTask task = null; 

    @Override 
    public void sendRequest(final HttpUriRequest request) { 
     this.task = new ConnectionTask(); 
     this.uiThreadHandler.post(new Runnable() { 
      public void run() { 
       task.executeOnExecutor(taskExecutor, request); 
      } 
     }); 
    } 

    @Override 
    public void cancel() { 
     if (this.task != null) 
      this.task.cancel(true); 
    } 
} 

此代碼可以讓我跑幾個HTTP並行連接不會在默認的AsyncTaskExecutor(這只是一個單線程隊列)上彼此阻塞。

我檢查了一下,AsyncTask s實際上是達到他們的onPostExecute()方法,並且不只是永遠運行。在檢查了一些內存轉儲後,我懷疑包裝Thread-在AsyncTasks完成後,對象不停止運行。

是否有可能上述代碼仍然是我的內存泄漏的責任,或者我應該開始尋找其他地方?

任何幫助表示讚賞。

編輯:應該指出,sendRequest只有一次被稱爲。代碼的其他部分不在上面的示例中,以確保這一點。

編輯2:超一流看起來是這樣的:

public abstract class AbstractUraHostConnection { 
    protected IUraHostConnectionListener listener = null; 

    public void setListener(IUraHostConnectionListener listener) { 
     this.listener = listener; 
    } 
    public abstract void sendRequest(HttpUriRequest request); 
    public abstract void cancel(); 
} 

的的AsyncTask看起來是這樣的:

private class ConnectionTask extends AsyncTask<HttpUriRequest, Object, Void> { 
    final byte[] buffer = new byte[2048]; 
    private ByteArrayBuffer receivedDataBuffer = new ByteArrayBuffer(524288); 

    @Override 
    protected Void doInBackground(HttpUriRequest... arg0) { 
     UraHostHttpConnection.taskCounter++; 
     AndroidHttpClient httpClient = AndroidHttpClient.newInstance("IVU.realtime.app"); 
     try { 
      // Get response and notify listener 
      HttpResponse response = httpClient.execute(arg0[0]); 
      this.publishProgress(response); 

      // Check status code OK before proceeding 
      if (response.getStatusLine().getStatusCode() == 200) { 
       HttpEntity entity = response.getEntity(); 
       InputStream inputStream = entity.getContent(); 
       int readCount = 0; 

       // Read one kB of data and hand it over to the listener 
       while ((readCount = inputStream.read(buffer)) != -1 && !this.isCancelled()) { 
        this.receivedDataBuffer.append(buffer, 0, readCount); 
        if (this.receivedDataBuffer.length() >= 524288 - 2048) { 
         this.publishProgress(receivedDataBuffer.toByteArray()); 
         this.receivedDataBuffer.clear(); 
        } 
       } 

       if (this.isCancelled()) { 
        if (arg0[0] != null && !arg0[0].isAborted()) { 
         arg0[0].abort(); 
        } 
       } 
      } 
     } catch (IOException e) { 
      // forward any errors to listener 
      e.printStackTrace(); 
      this.publishProgress(e); 
     } finally { 
      if (httpClient != null) 
       httpClient.close(); 
     } 

     return null; 
    } 

    @Override 
    protected void onProgressUpdate(Object... payload) { 
     // forward response 
     if (payload[0] instanceof HttpResponse) 
      listener.onReceiveResponse((HttpResponse) payload[0]); 
     // forward error 
     else if (payload[0] instanceof Exception) 
      listener.onFailWithException((Exception) payload[0]); 
     // forward data 
     else if (payload[0] instanceof byte[]) 
      listener.onReceiveData((byte[]) payload[0]); 
    } 

    @Override 
    protected void onPostExecute(Void result) { 
     listener.onReceiveData(this.receivedDataBuffer.toByteArray()); 
     listener.onFinishLoading(); 
     UraHostHttpConnection.taskCounter--; 
     Log.d(TAG, "There are " + UraHostHttpConnection.taskCounter + " running ConnectionTasks."); 
    } 
} 
+0

不能確定的大小,但能這樣幫助你嗎? http://www.androiddesignpatterns.com/2013/04/activitys-threads-memory-leaks.html – dumazy 2013-04-25 09:00:03

+1

AbstractUraHostConnection的超類的構造函數中的任何內容可能會令人驚訝?另外ConnectionTask看起來如何? – ddmps 2013-04-25 09:03:47

+0

添加了這兩個類的代碼。 – Chris 2013-04-25 09:09:29

回答

1

替換爲您執行人的ThreadPoolExecutor讓你有控制權游泳池的大小。如果ThreadPoolExecutor基本上是具有公開方法的Executor,則可能只是默認最大池大小設置得非常高的情況。

官方文檔here

採取在特定的一看:如果你要取決於編寫更少(更好的主意

setCorePoolSize(int corePoolSize) 
//Sets the core number of threads. 

setKeepAliveTime(long time, TimeUnit unit) 
//Sets the time limit for which threads may remain idle before being terminated. 

setMaximumPoolSize(int maximumPoolSize) 
//Sets the maximum allowed number of threads. 

還有另一種控制多少你真的想和你有多少代碼進行交易來獲得它)。

Executor taskExecutor = Executors.newFixedThreadPool(x); 

其中x =您的池

+0

謝謝。是的,這解決了這個問題。但出於好奇,我想知道我的代碼出了什麼問題。在一段時間後殺死沒有響應的線程並不是一個特別優雅的解決方案:-) – Chris 2013-04-25 09:27:23

+1

編輯替代選項的答案。線程*應該自動GCed,但正如我們所知,GC甚至在我們直接詢問時也會做GC的工作。我希望我對底層機制有更多的瞭解,但是他們不會把我稱爲Kludge ......另外,限制游泳池不應該讓你自己追捕殭屍。 – MarsAtomic 2013-04-25 09:29:48