2015-07-06 3 views
8

Ho cercato di inventare il codice più semplice per riprodurre quello che sto vedendo. Il programma completo è sotto, ma lo descriverò qui. Supponiamo che abbia classe denominata ListData che ha solo alcune proprietà. Quindi supponiamo di avere una classe MyList con un membro List<ListData> m_list. Supponiamo che m_list venga inizializzato nel costruttore MyList.Cos'è questo "oggetto pinning handle []" che vedo in Jetbrains dotMemory quando uso un elenco <T>?

Nel metodo principale, creo semplicemente uno di questi oggetti MyList, aggiungo alcuni ListData a esso, quindi lo lasciano fuori ambito. Faccio uno snapshot in dotMemory dopo che sono stati aggiunti ListData, quindi prendo un'altra istantanea dopo che l'oggetto MyList esce dallo scope.

In dotMemory posso vedere che l'oggetto MyList è stato recuperato come previsto. Vedo anche che i due oggetti ListData che ho creato sono stati recuperati come previsto.

Quello che non capisco è perché c'è un ListData[] che è sopravvissuto? Ecco una schermata di questo: oggetti Screenshot of this in dotMemory

ho aperto sopravvissuto sulla più recente snapshot per la ListData[] allora ho vista chiave di ritenzione Paths, questo è quello che vedo.

Key Retention Paths

Sono nuovo di gestione della memoria NET e ho creato questa applicazione di esempio per aiutarmi a esplorarlo. Ho scaricato la versione di prova di JetBrains dotMemory versione 4.3. Sto usando Visual Studio 2013 Professional. Devo imparare la gestione della memoria in modo da poter risolvere i problemi di memoria che abbiamo al lavoro.

Ecco il programma completo che può essere utilizzato per riprodurre questo. È solo un'app veloce e sporca, ma otterrà la cosa che sto chiedendo se la profilassi.

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 

namespace ConsoleApplication1 
{ 

    class ListData 
    { 
     public ListData(string name, int n) { Name = name; Num = n; } 
     public string Name { get; private set; } 
     public int Num { get; private set; } 
    } 

    class MyList 
    { 

     public MyList() 
     { 
      m = new List<ListData>(); 
     } 

     public void AddString(ListData d) 
     { 
      m.Add(d); 
     } 

     private List<ListData> m; 

    } 


    class Program 
    { 
     static void Main(string[] args) 
     { 
      { 
       MyList l = new MyList(); 
       bool bRunning = true; 
       while (bRunning) 
       { 
        Console.WriteLine("a or q"); 
        string input = Console.ReadLine(); 
        switch (input) 
        { 
         case "a": 
          { 
           Console.WriteLine("Name: "); 
           string strName = Console.ReadLine(); 
           Console.WriteLine("Num: "); 
           string strNum = Console.ReadLine(); 
           l.AddString(new ListData(strName, Convert.ToInt32(strNum))); 
           break; 
          } 
         case "q": 
          { 
           bRunning = false; 
           break; 
          } 
        } 
       } 
      } 

      Console.WriteLine("good bye"); 
      Console.ReadLine(); 
     } 
    } 
} 

Passi:

  1. Costruire sopra il codice nel rilascio.
  2. In dotMemory, selezionare per creare il profilo di un'app standalone .
  3. Passare al file di rilascio.
  4. Selezionare l'opzione per avviare immediatamente la raccolta dei dati di allocazione.
  5. Fare clic su Esegui.
  6. Prendere immediatamente un'istantanea e denominarla "prima". Questo è prima che siano aggiunti tutti i dati di lista .
  7. Nell'app, digitare a e aggiungere due ListData.
  8. In dotMemory, prendi un'altra istantanea e chiamala "aggiunta 2" perché abbiamo aggiunto due ListData.
  9. Nell'app, digitare q per uscire (la MyList andrà fuori campo). Prima di digitare nuovamente Invio per uscire dall'app, vai a un'altra istantanea in dotMemory. Chiamarlo "fuori ambito".
  10. Nell'app, digitare Invio per chiudere l'app.
  11. In dotMemory, confrontare le istantanee "aggiunte 2" e "fuori ambito" . Raggruppa per spazio dei nomi. Vedrai i ListData [] a cui mi riferisco.

Si noti che MyList e i due oggetti ListData sono stati recuperati dal garbage collector, ma ListData [] no. Perché c'è un ListData [] in giro? Come posso far sì che i rifiuti vengano raccolti?

risposta

3

Perché c'è un ListData [] in giro? Come posso far sì che venga raccolta la spazzatura ?

Se si guarda alla "creazione dello stack" dentro dotMemory, vedrete:

dotMemory StackTrace

Questo mostra che il ListData[0] istanza vuoto è stato creato attraverso il costruttore statico di List<T>. If you look at the source, vedrete questo:

static readonly T[] _emptyArray = new T[0];  

List<T> inizializza un default, array vuoto per ottimizzare l'evitare un tale ripartizione ogni volta che si crea un nuovo List<T>. Questo è il costruttore di default:

public List() 
{ 
    _items = _emptyArray; 
} 

solo si utilizza List<T>.Add, sarà ridimensionare la matrice.

static membri fanno riferimento allo "High Frequency Heap", che vengono creati una volta per ogni AppDomain nell'applicazione. Il numero di telefono object[] visualizzato è in realtà la posizione in cui sono memorizzate tutte le istanze static.

Poiché l'istanza è static, rimarrà in memoria per la durata della vostra applicazione.

+0

Yuval, grazie per la risposta. Sto cercando la tua risposta per capire e ti farò sapere se ho delle domande. – cchampion

+0

@cchampion Benvenuto. –

+0

Ok per assicurarmi di aver capito correttamente mi permetta di spiegare con parole mie e per favore correggimi se sbaglio. Quello che sto vedendo è che _emptyArray statico viene lasciato indietro. Poiché _emptyArray è statico, sarà sempre una radice per l'array vuoto e pertanto non verrà mai raccolta la garbage collection. – cchampion