2012-07-16 20 views
5

Avere un servizio Windows che, tra le altre cose, controlla i pool di applicazioni IIS. Se un pool ha configurato applicazioni e non è in esecuzione, viene avviato (il pool). Questo ha funzionato bene per qualche tempo. Recentemente è stato scoperto che il servizio stava perdendo memoria. Guardando un dump della memoria il colpevole è Microsoft.Web.Administration utilizzato per controllare i pool di applicazioni. L'unico oggetto che è usa e getta è ServerManager e lo ho in un blocco using. Ho trovato altre segnalazioni di questa fuga ma nessuna soluzione ancora. (vedere commenti degli utenti in http://msdn.microsoft.com/en-us/library/microsoft.web.administration.servermanager(v=vs.90).aspx#CommunityContent)Perdita di memoria Microsoft.Web.Administration

Quando si esegue il dump di tutte le radici per Microsoft.Web.Administration.ServerManager (481 in questo dump) vedo solo le radici una di esse. Supponiamo che questa sia l'iterazione corrente.

Non sono sicuro perché questi oggetti Web.Administration non possano essere raccolti, anche se non sembrano essere rootati (?). Qualche idea su come collegare questa perdita?

Ho spostato il codice su un'applicazione console e riprodotto la perdita lì.

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Threading; 
using Microsoft.Web.Administration; 

namespace Web.Administration.LeakExample 
{ 
    public static class ServerManagerExtensions 
    { 
     /// <summary> 
     /// Returns the application counts for application pools under the specified ServerManager. 
     /// Application pools without applications will not be returned. 
     /// </summary> 
     /// <param name="manager"></param> 
     /// <returns></returns> 
     public static Dictionary<string, uint> GetPoolApplicationCounts(this ServerManager manager) 
     { 
      if (manager == null) 
      { 
       throw new ArgumentNullException(); 
      } 

      var appCounts = new Dictionary<string, uint>(manager.ApplicationPools.Count); 
      foreach (var app in manager.Sites.SelectMany(site => site.Applications)) 
      { 
       if (!appCounts.ContainsKey(app.ApplicationPoolName)) 
       { 
        appCounts.Add(app.ApplicationPoolName, 1); 
       } 
       else 
       { 
        appCounts[app.ApplicationPoolName]++; 
       } 
      } 

      return appCounts; 
     } 
    } 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      while (!Console.KeyAvailable) 
      { 
       Console.WriteLine("Checking App Pools..."); 

       using (var manager = new ServerManager()) 
       { 
        var appCounts = manager.GetPoolApplicationCounts(); 

        var appPools = manager.ApplicationPools.Where(pool => appCounts.ContainsKey(pool.Name)); 

        foreach (
         var pool in 
          appPools.Where(
           pool => (pool.State != ObjectState.Started) && (pool.State != ObjectState.Starting))) 
        { 
         var state = pool.Start(); 

         if ((state == ObjectState.Started) || (state == ObjectState.Starting)) 
         { 
          Console.WriteLine("Started app pool \"{0}\"", pool.Name); 
         } 
         else 
         { 
          Console.WriteLine("Failed to start app pool \"{0}\". state:{1}", pool.Name, state); 
         } 
        } 
       } 

       Console.WriteLine("Sleeping..."); 
       Thread.Sleep(1000); 
      } 
     } 
    } 
} 

WinDbg sessione:

> !dumpheap -stat 

606b778c  479   5748 System.Runtime.Remoting.Activation.ConstructionLevelActivator 
00472088  480   5760 System.Collections.Generic.Dictionary`2+ValueCollection[[System.String, mscorlib],[Microsoft.Web.Administration.Interop.IAppHostElement, Microsoft.Web.Administration]] 
00471d5c  480   5760 System.Collections.Generic.SortedList`2+ValueList[[System.String, mscorlib],[Microsoft.Web.Administration.Configuration, Microsoft.Web.Administration]] 
00194568  481   5772 Web.Administration.LeakExample.Program+<>c__DisplayClass3 
00198438  480   7680 Microsoft.Web.Administration.Interop.AppHostWritableAdminManager 
0047199c  481   7696 System.Linq.Enumerable+<>c__DisplayClassf`1[[Microsoft.Web.Administration.ApplicationPool, Microsoft.Web.Administration]] 
606cc200  958  11496 System.Char 
00471e2c  479  11496 System.Collections.Generic.SortedList`2+SortedListValueEnumerator[[System.String, mscorlib],[Microsoft.Web.Administration.Configuration, Microsoft.Web.Administration]] 
0047090c  479  11496 System.Collections.Generic.List`1+Enumerator[[Microsoft.Web.Administration.Site, Microsoft.Web.Administration]] 
004706f0  958  11496 Microsoft.Web.Administration.ConfigurationElementCollectionBase`1+<>c__DisplayClass4[[Microsoft.Web.Administration.Site, Microsoft.Web.Administration]] 
00471aec  480  11520 System.Collections.Generic.List`1+Enumerator[[Microsoft.Web.Administration.ApplicationPool, Microsoft.Web.Administration]] 
0047041c  480  11520 System.Collections.Generic.List`1[[Microsoft.Web.Administration.Site, Microsoft.Web.Administration]] 
00196d58  480  11520 System.Collections.Generic.List`1[[Microsoft.Web.Administration.Configuration, Microsoft.Web.Administration]] 
004715cc  481  11544 System.Collections.Generic.List`1[[Microsoft.Web.Administration.ApplicationPool, Microsoft.Web.Administration]] 
0047103c  1437  17244 Microsoft.Web.Administration.ConfigurationElementCollectionBase`1+<>c__DisplayClass4[[Microsoft.Web.Administration.Application, Microsoft.Web.Administration]] 
606ccfc8  1438  17256 System.Int32 
00196a58  480  19200 System.Collections.Generic.SortedList`2[[System.String, mscorlib],[Microsoft.Web.Administration.Configuration, Microsoft.Web.Administration]] 
0019660c  480  21120 Microsoft.Web.Administration.ConfigurationManager 
00471130  958  22992 System.Collections.Generic.List`1+Enumerator[[Microsoft.Web.Administration.Application, Microsoft.Web.Administration]] 
00470d68  960  23040 System.Collections.Generic.List`1[[Microsoft.Web.Administration.Application, Microsoft.Web.Administration]] 
00198558  480  23040 System.Collections.Generic.Dictionary`2[[System.String, mscorlib],[Microsoft.Web.Administration.Interop.IAppHostElement, Microsoft.Web.Administration]] 
00196a08  481  23088 Microsoft.Web.Administration.Configuration 
00183170  481  23088 System.Collections.Generic.Dictionary`2[[System.String, mscorlib],[System.UInt32, mscorlib]] 
606b7574  479  24908 System.Runtime.Remoting.Messaging.ConstructorCallMessage 
00199e7c  479  24908 System.Linq.Enumerable+<SelectManyIterator>d__14`2[[Microsoft.Web.Administration.Site, Microsoft.Web.Administration],[Microsoft.Web.Administration.Application, Microsoft.Web.Administration]] 
00199384  480  28800 System.Collections.Generic.Dictionary`2+Entry[[System.String, mscorlib],[Microsoft.Web.Administration.Interop.IAppHostElement, Microsoft.Web.Administration]][] 
00470874  958  30656 System.Predicate`1[[Microsoft.Web.Administration.Site, Microsoft.Web.Administration]] 
004718a8  962  30784 System.Linq.Enumerable+WhereEnumerableIterator`1[[Microsoft.Web.Administration.ApplicationPool, Microsoft.Web.Administration]] 
001944d8  481  30784 Microsoft.Web.Administration.ServerManager 
00195178  963  30816 System.Func`2[[Microsoft.Web.Administration.ApplicationPool, Microsoft.Web.Administration],[System.Boolean, mscorlib]] 
606ccf90  968  31268 System.Int32[] 
00197a50  480  32640 Microsoft.Web.Administration.SiteCollection 
00194da0  481  32708 Microsoft.Web.Administration.ApplicationPoolCollection 
004719f8  2874  34488 Microsoft.Web.Administration.ConfigurationElementCollectionBase`1+<>c__DisplayClass4[[Microsoft.Web.Administration.ApplicationPool, Microsoft.Web.Administration]] 
0019810c  960  42240 Microsoft.Web.Administration.ConfigurationSection 
00471098  1437  45984 System.Predicate`1[[Microsoft.Web.Administration.Application, Microsoft.Web.Administration]] 
606cc1c8  961  46638 System.Char[] 
001838d0  481  59644 System.Collections.Generic.Dictionary`2+Entry[[System.String, mscorlib],[System.UInt32, mscorlib]][] 
606bedd0  5269  63228 System.UInt32 
00470a7c  960  69120 Microsoft.Web.Administration.ApplicationCollection 
00197c60  960  76800 Microsoft.Web.Administration.Site 
00197ea8  1440  86400 Microsoft.Web.Administration.Application 
00471a54  2874  91968 System.Predicate`1[[Microsoft.Web.Administration.ApplicationPool, Microsoft.Web.Administration]] 
6067c684  4832  141272 System.Object[] 
00195024  2886  196248 Microsoft.Web.Administration.ApplicationPool 
606c7b20 16323  261168 System.__ComObject 

>.foreach (obj {!dumpheap -mt 001944d8 -short}){!gcroot -all obj} 

    002ef124 003f0359 Web.Administration.LeakExample.Program.Main(System.String[]) [c:\Users\Chuck\Documents\Visual Studio 2012\Projects\Web.Administration.LeakExample\Web.Administration.LeakExample\Program.cs @ 74] 
     ebp+70: 002ef148 
      -> 02773270 System.Linq.Enumerable+WhereEnumerableIterator`1[[Microsoft.Web.Administration.ApplicationPool, Microsoft.Web.Administration]] 
      -> 02772948 Microsoft.Web.Administration.ApplicationPoolCollection 
      -> 02772060 Microsoft.Web.Administration.ServerManager 

    002ef124 003f0359 Web.Administration.LeakExample.Program.Main(System.String[]) [c:\Users\Chuck\Documents\Visual Studio 2012\Projects\Web.Administration.LeakExample\Web.Administration.LeakExample\Program.cs @ 74] 
     ebp+68: 002ef150 
      -> 02773270 System.Linq.Enumerable+WhereEnumerableIterator`1[[Microsoft.Web.Administration.ApplicationPool, Microsoft.Web.Administration]] 
      -> 02772948 Microsoft.Web.Administration.ApplicationPoolCollection 
      -> 02772060 Microsoft.Web.Administration.ServerManager 

    002ef124 003f0359 Web.Administration.LeakExample.Program.Main(System.String[]) [c:\Users\Chuck\Documents\Visual Studio 2012\Projects\Web.Administration.LeakExample\Web.Administration.LeakExample\Program.cs @ 74] 
     ebp+78: 002ef140 
      -> 027732c0 System.Linq.Enumerable+WhereEnumerableIterator`1[[Microsoft.Web.Administration.ApplicationPool, Microsoft.Web.Administration]] 
      -> 02772948 Microsoft.Web.Administration.ApplicationPoolCollection 
      -> 02772060 Microsoft.Web.Administration.ServerManager 

    002ef124 003f0359 Web.Administration.LeakExample.Program.Main(System.String[]) [c:\Users\Chuck\Documents\Visual Studio 2012\Projects\Web.Administration.LeakExample\Web.Administration.LeakExample\Program.cs @ 74] 
     ebp+6c: 002ef14c 
      -> 02773270 System.Linq.Enumerable+WhereEnumerableIterator`1[[Microsoft.Web.Administration.ApplicationPool, Microsoft.Web.Administration]] 
      -> 02772948 Microsoft.Web.Administration.ApplicationPoolCollection 
      -> 02772060 Microsoft.Web.Administration.ServerManager 

    002ef124 003f0359 Web.Administration.LeakExample.Program.Main(System.String[]) [c:\Users\Chuck\Documents\Visual Studio 2012\Projects\Web.Administration.LeakExample\Web.Administration.LeakExample\Program.cs @ 74] 
     ebp+74: 002ef144 
      -> 027732c0 System.Linq.Enumerable+WhereEnumerableIterator`1[[Microsoft.Web.Administration.ApplicationPool, Microsoft.Web.Administration]] 
      -> 02772948 Microsoft.Web.Administration.ApplicationPoolCollection 
      -> 02772060 Microsoft.Web.Administration.ServerManager 

    002ef124 003f0359 Web.Administration.LeakExample.Program.Main(System.String[]) [c:\Users\Chuck\Documents\Visual Studio 2012\Projects\Web.Administration.LeakExample\Web.Administration.LeakExample\Program.cs @ 74] 
     ebp+60: 002ef158 
      -> 02772948 Microsoft.Web.Administration.ApplicationPoolCollection 
      -> 02772060 Microsoft.Web.Administration.ServerManager 

    002ef124 003f0359 Web.Administration.LeakExample.Program.Main(System.String[]) [c:\Users\Chuck\Documents\Visual Studio 2012\Projects\Web.Administration.LeakExample\Web.Administration.LeakExample\Program.cs @ 74] 
     ebp+3c: 002ef17c 
      -> 02772060 Microsoft.Web.Administration.ServerManager 

    002ef124 003f0359 Web.Administration.LeakExample.Program.Main(System.String[]) [c:\Users\Chuck\Documents\Visual Studio 2012\Projects\Web.Administration.LeakExample\Web.Administration.LeakExample\Program.cs @ 74] 
     ebp+4c: 002ef16c 
      -> 027732c0 System.Linq.Enumerable+WhereEnumerableIterator`1[[Microsoft.Web.Administration.ApplicationPool, Microsoft.Web.Administration]] 
      -> 02772948 Microsoft.Web.Administration.ApplicationPoolCollection 
      -> 02772060 Microsoft.Web.Administration.ServerManager 

    002ef124 003f0359 Web.Administration.LeakExample.Program.Main(System.String[]) [c:\Users\Chuck\Documents\Visual Studio 2012\Projects\Web.Administration.LeakExample\Web.Administration.LeakExample\Program.cs @ 74] 
     ebp+40: 002ef178 
      -> 02773270 System.Linq.Enumerable+WhereEnumerableIterator`1[[Microsoft.Web.Administration.ApplicationPool, Microsoft.Web.Administration]] 
      -> 02772948 Microsoft.Web.Administration.ApplicationPoolCollection 
      -> 02772060 Microsoft.Web.Administration.ServerManager 

    002ef124 003f0359 Web.Administration.LeakExample.Program.Main(System.String[]) [c:\Users\Chuck\Documents\Visual Studio 2012\Projects\Web.Administration.LeakExample\Web.Administration.LeakExample\Program.cs @ 74] 
     ebp+50: 002ef168 
      -> 02772060 Microsoft.Web.Administration.ServerManager 

Found 10 roots. 

risposta

3

Quando ho letto la questione, la prima cosa che mi venne in mente fu che l'intera questione potrebbe essere evitato del tutto utilizzando la Windows Activation Service (WAS).

Il mio primo passaggio nel codice che hai postato mi ha dato la sensazione che il tuo problema potesse essere collegato all'accesso/modifica delle varie chiusure che vengono create dai loop foreach.

Prova queste piccole refactoring e vedere se uno/tutti sono in grado di ridurre o eliminare il problema o, in mancanza, fornisce ulteriori informazioni sulla causa principale:

  1. evitano differita l'esecuzione delle query LINQ chiamando .ToList() immediatamente
  2. Invece di riutilizzare l'oggetto ServerManager, creare un nuovo uno su ogni iterazione del ciclo
  3. Dal momento che si sta utilizzando solo ApplicationPool.Name, rifattorizziamo questo:

    foreach (var app in manager.Sites.SelectMany(site => site.Applications))

a questo:

foreach (var app in manager.Sites.SelectMany(site => site.Applications, 
    (s, a) => a.Name) 
    .ToList()) 

La mia ipotesi è che questo è ciò che sta accadendo:

  • nuova istanza di ServerManager viene creata nel using dichiarazione
  • Static il metodo di estensione è chiamato su quell'oggetto ref erence, che utilizza ApplicationPools internamente
  • ApplicationPools viene immediatamente riutilizzata nella costruzione di un IQueryable, che viene immediatamente utilizzato (ma non completamente eseguito - foreach dichiarazioni utilizzare il yield return internamente) e filtrata.
  • chiamata a .Start() modifiche (muta) stato del ApplicationPool a cui fa riferimento il riferimento all'oggetto ServerManager
  • il resto non è rilevante, in modo che il ciclo risale agli inizi. Usa ancora lo stesso ServerManager. Poiché tale riferimento ha uno stato mutato (a causa della chiamata a .Start()), potrebbe essere contrassegnato dal GC e mai raccolto: il pool di applicazioni non viene mai restituito nei risultati delle iterazioni successive e potrebbe causare l'orfano del riferimento in memoria.

Questa è solo un'ipotesi, tenete a mente!

1

Ora credo che questa non sia una vera perdita. Ho provato ToList() tutto come suggerito Josh. Questo non ha influenzato l'utilizzo della memoria.

Ho aggiunto un GC.Collect() al ciclo (solo a scopo di test) e questo ha eliminato la "perdita". Ho verificato questo facendo un dump della memoria, non ho trovato nessun oggetto Web.Application sullo heap. Questo coincide con il fatto che tutti gli oggetti Web.Application che ho visto nei miei dump precedenti erano in realtà solo root. Gli altri non erano radicati e in attesa di essere raccolti.

Sono stato bloccato dalla natura non deterministica del garbage collector .NET. Ci scusiamo per il rumore.

+1

Devi accettare questa risposta per chiudere la discussione. –

1

Ho trovato la soluzione qui: www.tomanderson.me.

si suggerisce di utilizzare

using (ServerManager sm = ServerManager.OpenRemote("localhost")) 
{ /*..*/ } 

Opere per me.

+0

non è buono come sembra come si può ottenere 'System.NotSupportedException: l'operazione specificata non è supportata quando viene specificato un nome server' a volte (ad es. Quando si tenta di aggiornare il binding https) –