2013-05-08 67 views
1

我一直在使用Task.Factory.FromAsync()方法,並且一直在經歷嚴重的內存泄漏。我用探查它表明,大量的對象似乎只是使用後遊逛:使用Task.Factory.FromAsync時泄漏

Heap shot 140 at 98.591 secs: size: 220177584, object count: 2803125, class count: 98, roots: 666 
     Bytes  Count Average Class name 
     25049168  142325  175 System.Threading.Tasks.Task<System.Int32> (bytes: +398816, count: +2266) 
      1 root references (1 pinning) 
      142324 references from: System.Threading.Tasks.Task 
      142305 references from: System.Threading.Tasks.TaskCompletionSource<System.Int32> 
      98309 references from: task_test.Task3Test.<Run>c__AnonStorey1 
     25049024  142324  176 System.Threading.Tasks.Task (bytes: +398816, count: +2266) 
      142304 references from: System.Threading.Tasks.TaskContinuation 
     17078880  142324  120 System.Action<System.Threading.Tasks.Task<System.Int32>> (bytes: +271920, count: +2266) 
      142324 references from: System.Threading.Tasks.TaskActionInvoker.ActionTaskInvoke<System.Int32> 
     17076600  142305  120 System.Runtime.Remoting.Messaging.MonoMethodMessage (bytes: +271680, count: +2264) 
      1 root references (1 pinning) 
      142304 references from: System.MonoAsyncCall 
     17076584  142305  119 System.AsyncCallback (bytes: +271920, count: +2266) 
      1 root references (1 pinning) 
      142304 references from: System.MonoAsyncCall 
     17076584  142305  119 System.Func<System.Int32> (bytes: +271920, count: +2266) 
      1 root references (1 pinning) 
      142305 references from: System.Func<System.IAsyncResult,System.Int32> 
      142304 references from: System.Runtime.Remoting.Messaging.AsyncResult 
      1 references from: System.Func<System.AsyncCallback,System.Object,System.IAsyncResult> 
     17076584  142305  119 System.Func<System.IAsyncResult,System.Int32> (bytes: +271920, count: +2266) 
      1 root references (1 pinning) 
      142305 references from: System.Threading.Tasks.TaskFactory.<FromAsyncBeginEnd>c__AnonStorey3A<System.Int32> 
     17076480  142304  120 System.Runtime.Remoting.Messaging.AsyncResult (bytes: +271800, count: +2265) 
      98461 references from: System.Object[] 

我試圖找出什麼類型的東西可能/可能不會出現阻止了從識別物體的gc不再被使用。 FromAsync返回一個從TaskCompletionSource獲得的Task對象,該對象具有一個類變量「source」,該變量包含Task從任務調用中獲取的值。

這裏是測試用例。它還包括使用StartNew()的情況,其中內存使用沒有爆炸。下面的初始Test3Task沒有使用ContinueWith,但是看看它是不是我們沒有清理的東西,我們把它放在了(沒有效果)。 [不,下面使用的聆聽變量是多餘的 - 有計劃,使測試更加聰明,但一做永遠是一樣好。]

using System; 
using System.Threading; 
using System.Threading.Tasks; 

namespace task_test 
{ 
    class MainClass 
    { 
      public static void Main (string[] args) 
      { 
        // Test3 - Leaky 
        var t = new Task3Test(); 

        // Test4 - Doesn't leak 
        // var t = new Task4Test(); 

        t.Run(); 

      } 
    } 

    public class BaseTask 
    { 
      public int GetRandomInt(int top) 
      { 
        Random random = new Random(); 

        return random.Next(1,top); 
      } 
    } 

    public class FibArgs 
    { 
      public byte[] data; 
      public int n; 
    } 

    public class Fib 
    { 
      public int Calculate(FibArgs args) 
      { 
        int n = args.n; 

        int a = 0; 
        int b = 1; 
        // In N steps compute Fibonacci sequence iteratively. 
        for (int i = 0; i < n; i++) 
        { 
          int temp = a; 
          a = b; 
          b = temp + b; 
        } 
        Console.WriteLine("ThreadId: {2}, fib({0}) = {1}", n, a, Thread.CurrentThread.GetHashCode()); 
        return a; 
      } 
    } 

    public class Task3Test : BaseTask 
    { 
      public void Run() 
      { 
        bool listening = true; 
        long i = 0; 
        while (listening) 
        { 
          i++; 

          Func<int> fun =() => { 
            int n = GetRandomInt(100); 
            Fib f = new Fib(); 
            FibArgs args = new FibArgs(); 
            args.n = n; 

            return f.Calculate(args); 
          }; 

          var t = Task<int>.Factory.FromAsync(fun.BeginInvoke, fun.EndInvoke, null); 
          t.ContinueWith(x => { 
                if (x.IsCompleted) { 
                  x.Dispose(); 
                  x = null; 
                } 
              } 
            ); 
        } 
      } 
    } 

    public class Task4Test : BaseTask 
    { 
      public void Run() 
      { 
        bool listening = true; 
        long i = 0; 
        while (listening) 
        { 
          int n = GetRandomInt(100); 
          Fib f = new Fib(); 
          FibArgs args = new FibArgs(); 
          args.n = n; 

          Task.Factory.StartNew(() => f.Calculate(args), TaskCreationOptions.LongRunning) 
            .ContinueWith(x => { 
              if(x.IsFaulted) 
              { 
                Console.WriteLine("OOPS, error!!!"); 
                x.Exception.Handle(_ => true); //just an example, you'll want to handle properly 

              } 
              else if(x.IsCompleted) 
              { 
                Console.WriteLine("Cleaning up task {0}", x.Id); 
                x.Dispose(); 
              } 
            } 
          ); 
        } 
      } 

    } 
} 
+0

我在Darwin/x86上使用SGen運行此操作,幾分鐘後內存使用情況非常穩定。然而,Boehm,它迅速升高。你在哪個平臺上使用哪種GC? – 2013-05-08 17:10:37

+0

我的同事和我在Ubuntu 12.04 64位上測試了類似的東西,並且看到了與GC相似的行爲。 – 2013-05-08 18:55:09

+0

同時運行。使用sgen獲得堆積,並且它在瞬間完成記憶。 – 2013-05-08 18:59:10

回答

0

我敢肯定,問題是你創建任務比完成更快,所以他們排隊。

+0

當我們每秒鐘敲擊幾次時,我們可以看到這一點與我們的http偵聽器包裝。 – 2013-05-09 14:29:06

+0

你確定你的任務能夠快速完成,不會排隊嗎?你能修改你的測試用例,以便它能夠證明你實際看到的問題嗎? – 2013-05-14 17:42:37

+0

我現在可以肯定地確認這個特定的測試用例由於任務排隊而泄漏內存。 – 2013-05-14 18:25:33