Rilevamento degli oggetti nell'API di profilatura
Garbage Collection recupera la memoria occupata dagli oggetti inutilizzati e può comprimere lo spazio liberato. Di conseguenza, gli oggetti attivi vengono spostati all'interno dell’heap. In questo argomento viene spiegato come lo spostamento di un oggetto influisce sui valori di ObjectID e come tali valori vengono registrati dall'API di analisi durante un’operazione di Garbage Collection di compattazione e non di compattazione.
Movimento di un oggetto
Quando gli oggetti vengono spostati, cambiano i valori di ObjectID assegnati dalle notifiche precedenti. Lo stato interno dell'oggetto stesso non cambia, a parte eventuali riferimenti ad altri oggetti. Cambia soltanto la posizione in memoria dell’oggetto (e quindi il suo ObjectID). La notifica di ICorProfilerCallback::MovedReferences consente a un profiler di aggiornare le tabelle interne che registrano le informazioni in base a ObjectID. Il nome del metodo MovedReferences è fuorviante, in quanto viene generato anche per gli oggetti che non sono stati spostati.
Il numero di oggetti nell'heap può arrivare a più migliaia o milioni. Non sarebbe pratico registrare il movimento di un gran numero di oggetti fornendo per ogni oggetto un ID precedente e uno successivo allo spostamento. Di conseguenza, Garbage Collector cerca di spostare gli oggetti attivi contigui in blocchi, in modo che restino contigui anche nelle nuove posizioni nell’heap. La notifica MovedReferences indica l’ObjectID precedente e successivo allo spostamento di questi blocchi contigui di oggetti.
Si supponga che il valore di un ObjectID esistente (oldObjectID) si trovi all'interno dell'intervallo seguente:
oldObjectIDRangeStart[i] <= oldObjectID < oldObjectIDRangeStart[i] + cObjectIDRangeLength[i]
In questo caso, l'offset dall'inizio dell'intervallo all'inizio dell'oggetto è il seguente:
oldObjectID - oldObjectRangeStart[i]
Per qualsiasi valore di i che si trova nell'intervallo seguente:
0 <= i < cMovedObjectIDRanges
è possibile calcolare il nuovo ObjectID nel modo seguente:
newObjectID = newObjectIDRangeStart[i] + (oldObjectID – oldObjectIDRangeStart[i])
Tutti i callback sono eseguiti mentre Language Runtime comune (CLR) è sospeso. Pertanto, nessuno dei valori di ObjectID può variare fino alla ripresa del runtime e fino a quando non ha luogo un’altra operazione di Garbage Collection.
Nella figura seguente vengono mostrati 10 oggetti prima di Garbage Collection. Gli indirizzi iniziali (equivalenti a ObjectID) sono 08, 09, 10, 12, 13, 15, 16, 17, 18 e 19. Gli oggetti con ObjectID 09, 13 e 19 sono inutilizzati e il relativo spazio viene recuperato durante il processo di Garbage Collection.
Spostamento di un oggetto durante l’operazione di Garbage Collection
La parte inferiore della figura mostra gli oggetti dopo l’operazione di Garbage Collection. Lo spazio occupato dagli oggetti inutilizzati è stato recuperato per essere utilizzato da oggetti attivi. Nella figura, gli oggetti attivi nell’heap sono stati spostati nelle nuove posizioni. Di conseguenza, gli ObjectID cambieranno. La tabella seguente mostra gli ObjectID prima e dopo l’operazione di Garbage Collection.
Oggetto |
oldObjectIDRangeStart [] |
newObjectIDRangeStart [] |
---|---|---|
0 |
08 |
07 |
1 |
09 |
|
2 |
10 |
08 |
3 |
12 |
10 |
4 |
13 |
|
5 |
15 |
11 |
6 |
16 |
12 |
7 |
17 |
13 |
8 |
18 |
14 |
9 |
19 |
La tabella seguente comprime le informazioni specificando le posizioni iniziali e le dimensioni dei blocchi contigui. Questa tabella mostra con precisione come vengono indicate le informazioni dal metodo MovedReferences.
Blocchi |
oldObjectIDRangeStart [] |
newObjectIDRangeStart [] |
cObjectIDRangeLength [] |
---|---|---|---|
0 |
08 |
07 |
1 |
1 |
10 |
08 |
2 |
2 |
15 |
11 |
4 |
Rilevazione di tutti gli oggetti eliminati
Il metodo MovedReferences indica tutti gli oggetti che vengono conservati dopo un’operazione di Garbage Collection di compattazione, indipendentemente dal fatto che siano stati spostati. Qualsiasi oggetto non indicato da MovedReferences non viene conservato. Tuttavia, non tutte le Garbage Collection sono di compattazione. In .NET Framework versioni 1.0 e 1.1, il profiler non poteva rilevare gli oggetti che venivano conservati da una Garbage Collection non di compattazione (una Garbage Collection nella quale non viene spostato alcun oggetto). .NET Framework versione 2.0 dispone di un migliore supporto per questo scenario tramite i nuovi metodi seguenti:
Il profiler può chiamare il metodo ICorProfilerInfo2::GetGenerationBounds per ottenere i limiti dei segmenti dell’heap di Garbage Collection. Il campo rangeLength nella struttura COR_PRF_GC_GENERATION_RANGE risultante può essere utilizzato per determinare l'ambito degli oggetti attivi in una generazione di compattazione.
Il callback di ICorProfilerCallback2::GarbageCollectionStarted indica quali generazioni vengono raccolte dall’operazione di Garbage Collection corrente. Tutti gli oggetti che sono in una generazione che non è stata raccolta verranno conservati dopo un’operazione di Garbage Collection.
Il callback di ICorProfilerCallback2::SurvivingReferences indica quali oggetti verranno conservati dopo una Garbage Collection di non compattazione.
Si noti che una stessa Garbage Collection può essere di compattazione per una generazione e non di compattazione per un'altra. In altri termini, ogni specifica generazione riceverà il callback SurvivingReferences o MovedReferences per una specifica Garbage Collection, ma non entrambi.
Osservazioni
Dopo un’operazione di Garbage Collection, le applicazioni vengono arrestate fino a quando il runtime non ha completato il passaggio di informazioni sull’heap al Code Profiler. È possibile utilizzare il metodo ICorProfilerInfo::GetClassFromObject per ottenere il ClassID della classe dell'oggetto. È possibile utilizzare il metodo ICorProfilerInfo::GetClassIDInfo o ICorProfilerInfo2::GetClassIDInfo2 per ottenere informazioni dei metadati sulla classe.
In .NET Framework versioni 1.0 e 1.1, al termine di un'operazione di Garbage Collection, si prevede che ogni oggetto conservato sia un riferimento radice, abbia un padre che è un riferimento radice o esista in una generazione che non è stata raccolta. In alcuni casi, è possibile avere oggetti che non appartengono ad alcuna di queste categorie. Questi oggetti sono allocati internamente dal runtime o sono riferimenti deboli ai delegati. In .NET Framework versioni 1.0 e 1.1, l'API di analisi non permette all'utente di identificare questi oggetti.
In .NET Framework versione 2.0, sono stati aggiunti tre metodi per aiutare il profiler a chiarire con precisione quali generazioni vengono raccolte e quando nonché identificare quali oggetti sono di tipo radice. Questi metodi consentono al profiler di determinare perché alcuni oggetti vengono conservati dopo una raccolta:
Il metodo ICorProfilerCallback2::RootReferences2 consente al profiler di identificare gli oggetti conservati tramite handle speciali. Le informazioni dei limiti di una generazione fornite dal metodo ICorProfilerInfo2::GetGenerationBounds combinate con le informazioni della generazione raccolta fornite dal metodo ICorProfilerCallback2::GarbageCollectionStarted consentono al profiler di identificare gli oggetti che esistono in generazioni che non sono state raccolte.
Riferimento
Metodo ICorProfilerCallback::MovedReferences
Metodo ICorProfilerCallback2::SurvivingReferences
Metodo ICorProfilerInfo2::GetGenerationBounds