練習 2 - 追蹤使用者模式程序配置

堆積配置是透過堆積API (HeapAlloc、HeapRealloc和 C/C++ 配置直接進行,例如new、alloc、realloccalloc) ,並使用三種類型的堆積來提供服務:

  1. 主線 NT 堆積 – 小於 64 KB 的服務配置要求。

  2. 低片段堆積 – 由服務固定大社區塊配置要求的子區段所組成。

  3. VirtualAlloc – 大於 64 KB 之大小的服務配置要求。

VirtualAlloc 用於直接透過 VirtualAlloc API 建立的大型動態記憶體配置。 一般使用方式通常是點陣圖或緩衝區。 您可以使用 VirtualAlloc 來保留頁面區塊,然後對 VirtualAlloc 進行其他呼叫,以認可保留區塊中的個別頁面。 這可讓進程保留其虛擬位址空間的範圍,而不需要耗用實體儲存體,直到需要為止。

在此區域中有兩個概念可供瞭解:

  1. 保留記憶體:保留位址範圍以供使用,但不會取得記憶體資源。

  2. 認可記憶體:確保參考位址時,實體記憶體或分頁檔案空間都可供使用。

在此練習中,您將瞭解如何收集追蹤,以調查使用者模式進程如何配置記憶體。

此練習著重于稱為 「MemoryTestApp.exe 」的虛擬測試程式,可透過下列方式配置記憶體:

  1. 要認可大型記憶體緩衝區的 VirtualAlloc API。

  2. 要具現化小型物件的 C++ 運算子。

您可以從這裡下載MemoryTestApp.exe

步驟 1:使用 WPR 收集 virtualAlloc/heap 追蹤

大型記憶體配置通常是影響進程使用量,並由 VirtualAlloc API 提供服務的記憶體配置。 這是所有調查應該開始的地方,但進程也可能因為較小的配置而錯誤, (例如,在 C++ 中使用 運算子的記憶體流失等等。) 。 當這種情況發生時,堆積追蹤會變得很有用。

步驟 1.1:準備系統以進行堆積追蹤

VirtualAlloc 分析未提供記憶體使用量問題的任何相關說明時,堆積追蹤應該視為選擇性且完成。 堆積追蹤通常會產生較大的追蹤,建議只針對您要調查的個別進程啟用追蹤。

在此案例中,為感興趣的程式新增登錄機碼 (MemoryTestApp.exe) ;接著會針對每個後續的程式建立啟用堆積追蹤。

reg add "HKLM\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\MemoryTestApp.exe" /v TracingFlags /t REG_DWORD /d 1 /f

步驟 1.2:使用 WPR 擷取追蹤

在此步驟中,您將使用包含VirtualAllocHeap資料的WPR來收集追蹤。

  1. 開啟 WPR 並修改追蹤組態。

    1. 選取 VirtualAlloc堆積 提供者。

    2. 選取 [一般 ] 作為 效能案例

    3. 選取 [一般 ] 作為 記錄模式

      WPR 追蹤選項功能表的螢幕擷取畫面。

  2. 按一下 [開始] 開始追蹤。

  3. 啟動 MemoryTestApp.exe,並等候進程終止 (大約需要 30 秒) 。

  4. 返回WPR、儲存追蹤,並使用Windows 效能分析器 (WPA) 加以開啟。

  5. 開啟 [追蹤] 功能表,然後選取 [ 設定符號路徑]。

    • 指定符號快取的路徑。 如需符號的詳細資訊,請參閱 MSDN 上的 符號支援 頁面。
  6. 開啟 [追蹤] 功能表,然後選取 [載入符號]。

您現在有一個追蹤,其中包含 MemoryTestApp.exe 進程在其存留期內的所有記憶體配置模式。

步驟 2:檢閱 VirtualAlloc 動態配置

詳細的 VirtualAlloc 資料會透過 WPA 中的 'VirtualAlloc Commit Lifetimes' 圖表公開。 感興趣的索引鍵資料行如下:

資料行 描述
處理

透過 VirtualAlloc執行記憶體配置的進程名稱。

認可堆疊

呼叫堆疊,顯示導致配置記憶體的程式碼路徑。

認可時間

配置記憶體時的時間戳記。

取消認可時間

釋放記憶體時的時間戳記。

影響大小

未處理配置的大小,或所選時間間隔的開始和結束之間的大小差異。 此大小會根據選取的檢視埠進行調整。

如果進程配置的所有記憶體都由WPA中視覺化間隔的結尾釋放,影響大小值將會是零。

大小

所選時間間隔期間所有配置的累計總和。

請遵循下列步驟來分析 MemoryTestApp.exe

  1. Graph總管的[記憶體] 類別中尋找VirtualAlloc 認可存留期圖表。

  2. VirtualAlloc 認可存留期 拖放到 [ 分析 ] 索引標籤上。

  3. 組織資料表以顯示這些資料行。 以滑鼠右鍵按一下要加入或移除資料行的資料行標頭。

    1. 處理

    2. 影響類型

    3. 認可堆疊

    4. 認可時間和取消認可時間

    5. Count

    6. 影響大小大小

  4. 在進程清單中尋找 MemoryTestApp.exe

  5. 套用篩選,只保留圖形上的 MemoryTestApp.exe

    • 以滑鼠右鍵按一下,然後選取 [ 篩選 至選取範圍]。

顯示如何篩選結果的螢幕擷取畫面。

您的分析檢視區看起來應該像這樣:

篩選時資料外觀的範例圖表。

在上述範例中,有兩個值感興趣:

  • 大小 為 126 MB:這表示 MemoryTestApp.exe 在其生命週期的生命週期內總共配置了 125 MB。 它代表進程及其相依性所發出之所有 VirtualAlloc API 呼叫的累計總和。

  • 影響大小 0 MB:這表示進程配置的所有記憶體都會在目前分析的時間間隔結束時釋放。 系統不會因為其穩定狀態記憶體使用量增加而受到影響。

步驟 2.1:分析穩定狀態記憶體使用量

調查記憶體配置時,您應該嘗試回答問題:「為什麼此案例的穩定狀態記憶體使用量成長?」 在 MemoryTestApp.exe 範例中,您可以看到它在開頭配置了大約 10 MB 的穩定狀態記憶體,然後增加至 20 MB。

顯示記憶體使用量的範例資料的螢幕擷取畫面。

若要調查此行為,請在追蹤中間突然增加時,將縮放縮小到時間間隔的周圍。

顯示如何放大資料的螢幕擷取畫面。

您的檢視區看起來應該像這樣。

使用 VirtualAlloc Commit LifeTimes 和 Outstanding Commit by Process 套用縮放選項 shpwing 圖形之後範例資料的螢幕擷取畫面

如您所見, 影響大小 現在是 10 MB。 這表示,在分析時間間隔的開始和結尾之間,穩定狀態記憶體使用量會增加 10 MB。

  1. 按一下資料行標頭,依 [影響大小 ] 排序。

  2. 展開 [處理] 資料行) 中的[MemoryTestApp.exe] 資料列 (。

  3. 展開 [影響類型] 資料行) 中的[影響] 資料列 (。

  4. 流覽進程 認可堆疊 ,直到您找到配置 10 MB 記憶體的函式為止。

    範例資料表的螢幕擷取畫面,其中顯示行號、進程、影響類型、認可堆疊、認可時間、取消認可時間、計數和影響大小

在此範例中,MemoryTestApp.exe的 Main函式會直接呼叫VirtualAlloc,在工作負載中間配置 10 MB 的記憶體。 在真實世界中,應用程式開發人員應該判斷配置是否合理,或是否可以重新排列程式碼,以將穩定狀態記憶體使用量增加降到最低。

您現在可以取消 移動 WPA 中的檢視區。

Unzoom 功能表的 Screenhsot。

步驟 2.2:分析暫時性 (或尖峰) 記憶體使用量

調查記憶體配置時,您應該嘗試回答問題:「為什麼此案例的記憶體使用量有暫時性尖峰?」 暫時性配置會導致記憶體使用量尖峰,而且在記憶體壓力不足時,可能會導致分散和將寶貴的內容推送至系統待命快取。

MemoryTest 範例中,您可以看到有 10 個不同的記憶體使用量尖峰, (10 MB) 平均散佈在追蹤中。

顯示記憶體使用量資料的圖表螢幕擷取畫面。

將縮放縮小到最後四個尖峰,以專注于感興趣的較社區域,並減少非相關行為的雜訊。

縮放選項的螢幕擷取畫面。

您的檢視區看起來應該像這樣:

顯示使用縮放選項之記憶體使用量資料的圖表螢幕擷取畫面。

  1. 按一下資料行標頭,依 [大小 ] 排序。

  2. 展開 [處理] 資料行) 中的MemoryTestApp.exe列 (。

  3. 按一下 [影響類型] 資料行) 中的[暫時性資料列] (。

    • 這應該以藍色醒目提示檢視區中的所有記憶體使用量尖峰。
  4. 請注意不同資料行的值:

    1. 計數 = 4:這表示該時間間隔期間已進行四個暫時性記憶體配置。

    2. 影響大小 = 0 MB:這表示所有四個暫時性記憶體配置都會在時間間隔結束時釋出。

    3. 大小 = 40 MB:這表示所有四個暫時性記憶體配置的總和相當於 40 MB 的記憶體。

  5. 流覽進程 認可堆疊 ,直到您找到配置 40 MB 記憶體的函式為止。

    記憶體使用量資料的螢幕擷取畫面。

在此範例中,MemoryTestApp.exeMain函式會呼叫名為Operation1的函式,接著會呼叫名為ManipulateTemporaryBuffer的函式。 此 ManipulateTemporaryBuffer 函式接著會直接呼叫 VirtualAlloc 四次,每次建立並釋放 10 MB 記憶體緩衝區。 每個緩衝區只會過去 100 毫秒。 緩衝區的配置和可用時間會以認可時間和取消認可時間資料行表示。

在真實世界中,應用程式開發人員會判斷是否需要這些短期暫時性暫時性緩衝區配置,或是否可以使用永久記憶體緩衝區來取代作業。

您現在可以取消 移動WPA中的檢視區。

步驟 3:檢閱堆積動態配置

到目前為止,分析只會著重于 VirtualAlloc API 所服務的大型記憶體配置。 下一個步驟是使用一開始收集的堆積資料,判斷程式所做的其他小型配置是否有問題。

詳細的堆積資料會透過 WPA 中的 「堆積配置」 圖表公開。 感興趣的索引鍵資料行如下:

資料行 描述
處理 執行記憶體配置的進程名稱。
Handle

用來服務配置之堆積的識別碼。

可以建立堆積,因此進程可能會有多個堆積控制碼。

堆疊 呼叫堆疊,顯示導致配置記憶體的程式碼路徑。
配置時間 配置記憶體時的時間戳記。
影響大小 未完成配置的大小,或所選檢視區開始和結尾之間的差異。 此大小會根據選取的時間間隔進行調整。
大小 所有配置/解除配置的累計總和。

請遵循下列步驟來分析 MemoryTestApp.exe

  1. 在圖形總管的[記憶體] 類別中尋找堆積配置圖表

  2. 堆積配置 拖放到 [分析 ] 索引標籤上。

  3. 組織資料表以顯示這些資料行:

    1. 處理

    2. Handle

    3. 影響類型

    4. 堆疊

    5. AllocTime

    6. Count

    7. 影響大小大小

  4. 在進程清單中尋找 MemoryTestApp.exe

  5. 套用篩選準則,只保留圖形上的 MemoryTestApp.exe

    • 以滑鼠右鍵按一下並選取 [ 篩選 至選取範圍]。

您的檢視區看起來應該像這樣:

顯示依進程和控制碼之未完成大小的堆積配置圖表範例資料的螢幕擷取畫面

在此範例中,您可以看到其中一個堆積會以固定速率穩定增加一段時間的大小。 該堆積上有 1200 個記憶體配置,以間隔結束時計算 130 KB 的已使用記憶體。

  1. 放大較小的間隔 (例如,追蹤中間) 10 秒。

  2. 展開顯示最大配置的前端 控制碼 (,如 [影響大小 ] 資料行) 所示。

  3. 展開 [影響 類型]。

  4. 流覽進程 堆疊 ,直到您找到負責配置所有記憶體的函式為止。

    顯示進程、控制碼、影響類型、Stack、AllocTime、Count、影響大小和 Size 的範例資料表螢幕擷取畫面,其中已選取兩個數據列

在此範例中,MemoryTestApp.exeMain函式會呼叫名為InnerLoopOperation 的函式。 這個 InnerLoopOperation 函式接著會透過 C++ 運算子配置 40 個位元組的記憶體 319 次。 此記憶體會維持配置,直到進程終止為止。

在真實世界中,應用程式開發人員應該接著判斷此行為是否表示可能的記憶體流失,並修正問題。

步驟 4:清除測試系統

分析完成後,您應該清除登錄,以確定進程已停用堆積追蹤。 在提升許可權的命令提示字元上執行此命令:

reg delete "HKLM\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\MemoryTestApp.exe" /v TracingFlags /f