Views: 0
🔍 記憶體去哪了?忠碁科技專用診斷工具,教你揪出消失的實體記憶體!
📖 故事的開頭…
最近電腦開機一段時間後,發現記憶體使用了近 90%,但打開工作管理員把所有進程(Processes)的記憶體加總,卻怎麼也湊不到那個數字。
忠碁為了提供相關資訊給其它智慧工具分析,並找出到底是哪個神祕黑洞吃掉了資源,忠碁參考了 網路技術文章 的分析邏輯,編寫了這支「Windows 記憶體耗盡診斷腳本」。它不只看表面數據,更能深入內核層級(Kernel)去偵測那些連工作管理員都抓不到的記憶體佔用。
💡 為什麼你需要這支腳本?(亮點分析)
一般的系統工具只能看到應用程式的佔用,但當你遇到核心層級的問題時,往往無能為力。忠碁科技 在處理企業級系統維護時,常遇到以下「記憶體黑洞」:
- 驅動程式洩漏 (Driver Leak):非分頁池(Non-Paged Pool)異常飆高,通常是網卡或顯卡驅動作怪。
- 網路驅動 NDU 衝突:Windows 內置監控導致的記憶體洩漏。
- 句柄洩漏 (Handle Leak):程式碼沒寫好,瘋狂索取控制權而不歸還。
- IO 瓶頸:大量修改中頁面(Modified Page)堆積。
✨ 本程式 5 大核心亮點:
- 🚀 自動權限提升:內建管理員權限請求邏輯,確保獲取內核敏感數據。
- 🧩 隱形佔用精確計算 ($gap):自動對比系統總量與進程總量,直接算出被核心、快取吃掉的差距(Gap)。
- 🚨 智慧診斷與預警:
- 非分頁池預警:自動判別是否超過 1GB/2GB 臨界點,並篩選出可能的第三方驅動。
- 句柄監控:當單一進程 Handle 超過 10,000 個(例如特定印表機驅動異常),立即觸發警告。
- 📊 專業級數據報告:執行完畢自動生成 Memory_Report.txt 與 Memory_Summary.txt,格式美觀,方便傳給 忠碁 的技術人員進行深度分析。
- 🛠️ 實戰派建議:不僅發現問題,更會依據結果引導你使用 poolmon.exe 或 RAMMap 等進階工具。
📝 腳本源碼分享(PowerShell)
請將以下代碼複製並另存為 Memory_Diagnosis.ps1 後執行:
# --- 1. 自動提升權限 ---
$currentPrincipal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())
if (-not $currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
Write-Host ">>> 權限不足,正在請求管理員權限..."
$argList = "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`""
try { Start-Process powershell.exe -ArgumentList $argList -Verb RunAs; exit } catch { exit }
}
# --- 2. 路徑設定 ---
$reportPath = "$PSScriptRoot\Memory_Report.txt"
$summaryPath = "$PSScriptRoot\Memory_Summary.txt"
$report = New-Object System.Collections.Generic.List[string]
Write-Host ""
Write-Host "===== 記憶體耗盡診斷 (使用率分析) ====="
Write-Host ""
try {
# ========== 1. 基礎記憶體數據 ==========
$os = Get-CimInstance Win32_OperatingSystem
if ($null -eq $os) { throw "無法取得 Win32_OperatingSystem 資訊" }
$totalKB = $os.TotalVisibleMemorySize
$freeKB = $os.FreePhysicalMemory
$usedKB = $totalKB - $freeKB
$totalGB = [math]::round($totalKB / 1MB, 2)
$freeGB = [math]::round($freeKB / 1MB, 2)
$usedGB = [math]::round($usedKB / 1MB, 2)
$usagePercent = [math]::round(($usedKB / $totalKB) * 100, 2)
$bootTime = $os.LastBootUpTime
$uptime = (Get-Date) - $bootTime
$report.Add("[1] 系統基本資訊")
$report.Add(" 開機時間 : $bootTime")
$report.Add(" 運行時間 : $($uptime.Days)天 $($uptime.Hours)時 $($uptime.Minutes)分")
$report.Add(" 總記憶體 : $totalGB GB")
$report.Add(" 已用記憶體: $usedGB GB")
$report.Add(" 可用記憶體: $freeGB GB")
$report.Add(" 總體佔用率: $usagePercent %")
$report.Add("")
# ========== 2. 核心層級記憶體分類 ==========
$memPerf = Get-CimInstance Win32_PerfFormattedData_PerfOS_Memory -ErrorAction SilentlyContinue
if ($null -eq $memPerf) {
$report.Add("[2] 核心層級記憶體分類 - 警告: 無法讀取效能計數器")
$nonPagedMB = 0
$pagedMB = 0
$cacheMB = 0
$standbyMB = 0
$modifiedMB = 0
} else {
$nonPagedMB = [math]::round($memPerf.PoolNonpagedBytes / 1MB, 2)
$pagedMB = [math]::round($memPerf.PoolPagedBytes / 1MB, 2)
$cacheMB = [math]::round($memPerf.CacheBytes / 1MB, 2)
$standbyMB = [math]::round($memPerf.StandbyCacheCoreBytes / 1MB, 2)
$modifiedMB = [math]::round($memPerf.ModifiedPageListBytes / 1MB, 2)
$report.Add("[2] 核心層級記憶體分類 (找出隱形耗用)")
$report.Add(" 非分頁池 (NonPaged) : $nonPagedMB MB (超過 1000MB 需注意)")
$report.Add(" 分頁池 (Paged) : $pagedMB MB")
$report.Add(" 系統快取 (Cache) : $cacheMB MB")
$report.Add(" 待命記憶體 (Standby): $standbyMB MB")
$report.Add(" 修改中頁面 (Modified): $modifiedMB MB")
}
$report.Add("")
# ========== 3. 非分頁池異常診斷 (記憶體洩漏) ==========
if ($nonPagedMB -gt 2000) {
$report.Add("[3a] 嚴重警告:非分頁池高達 $nonPagedMB MB (正常值 < 1GB)")
$report.Add(" 這表示驅動程式或核心模組有嚴重記憶體洩漏!")
$report.Add("")
$drivers = Get-CimInstance Win32_SystemDriver | Where-Object { $_.State -eq "Running" -and $_.PathName -notlike "*\\system32\\drivers\\*" }
$report.Add(" 正在執行的第三方驅動程式 (前20個,可能是元兇):")
$drivers | Select-Object -First 20 | ForEach-Object {
$report.Add(" - $($_.Name) : $($_.PathName)")
}
if ($drivers.Count -gt 20) {
$report.Add(" ... 共 $($drivers.Count) 個,已省略 $($drivers.Count - 20) 個")
}
$report.Add("")
}
# ========== 4. 虛擬記憶體與分頁檔 ==========
$pageFile = Get-CimInstance Win32_PageFileUsage
$pageTotalGB = if ($pageFile) { [math]::round($pageFile.AllocatedBaseSize / 1024, 2) } else { 0 }
$pageUsedGB = if ($pageFile) { [math]::round($pageFile.CurrentUsage / 1024, 2) } else { 0 }
$report.Add("[4] 虛擬記憶體與分頁檔")
$report.Add(" 分頁檔總大小 : $pageTotalGB GB")
$report.Add(" 分頁檔已用 : $pageUsedGB GB")
$report.Add(" 提交總量 (Commit) : $([math]::round($os.TotalAllocatedBaseSize / 1KB / 1024, 2)) GB")
$report.Add(" 提交限制 (Limit) : $([math]::round($os.TotalVirtualMemorySize / 1MB, 2)) GB")
$report.Add("")
# ========== 5. NDU 網路驅動檢查 ==========
$report.Add("[5] 網路驅動程式與 NDU 狀態")
$ndu = Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\Ndu" -ErrorAction SilentlyContinue
$nduStart = if ($ndu) { $ndu.Start } else { "未找到" }
$report.Add(" NDU 服務啟動類型 : $nduStart (4為禁用,建議設為4)")
$netDrivers = Get-CimInstance Win32_PnPSignedDriver | Where-Object {
$_.DeviceName -match "Killer|Realtek|Intel.*Ethernet|Broadcom|VMware|VirtualBox|Hyper-V" -and $_.IsRunning
}
foreach ($net in $netDrivers) {
$report.Add(" 網卡驅動 : $($net.DeviceName) | 版本 : $($net.DriverVersion)")
}
$report.Add("")
# ========== 6. 進程記憶體排行 ==========
$report.Add("[6] 進程記憶體排行 (WorkingSet - 前 20 名)")
$report.Add(" 名稱 | 數量 | WorkingSet | 私用記憶體 | 句柄數")
$report.Add(" ----------------------------------------------------------------")
$processes = Get-Process | Group-Object Name | ForEach-Object {
$wsSum = ($_.Group | Measure-Object WorkingSet -Sum).Sum
$pmSum = ($_.Group | Measure-Object PrivateMemorySize -Sum).Sum
$hdSum = ($_.Group | Measure-Object HandleCount -Sum).Sum
[PSCustomObject]@{
Name = $_.Name
Count = $_.Count
WorkingSet_MB = [math]::Round($wsSum / 1MB, 2)
Private_MB = [math]::Round($pmSum / 1MB, 2)
Handles = $hdSum
}
} | Sort-Object WorkingSet_MB -Descending
$processes | Select-Object -First 20 | ForEach-Object {
$report.Add(" $($_.Name.PadRight(25)) | $($_.Count.ToString().PadLeft(4)) | $($_.WorkingSet_MB.ToString().PadLeft(8)) MB | $($_.Private_MB.ToString().PadLeft(8)) MB | $($_.Handles)")
}
$totalProcessWS = [math]::Round(($processes | Measure-Object WorkingSet_MB -Sum).Sum, 2)
$gap = [math]::Round($usedGB * 1024 - $totalProcessWS, 2)
$report.Add("")
$report.Add(" 所有進程 WorkingSet 總和 : $totalProcessWS MB")
$report.Add(" 與系統回報已使用量差距 : $gap MB (這個差距就是核心/驅動/快取吃掉的部分)")
$report.Add("")
# ========== 7. Handle 句柄排行 ==========
$report.Add("[7] 進程句柄排行 (Handles - 前 10 名)")
$report.Add(" (句柄數異常增加通常代表程式即將耗盡資源)")
$processes | Sort-Object Handles -Descending | Select-Object -First 10 | ForEach-Object {
$report.Add(" $($_.Name.PadRight(25)) | 句柄數 (Handles): $($_.Handles)")
}
$report.Add("")
# ========== 8. 句柄洩漏診斷 ==========
$topHandle = $processes | Sort-Object Handles -Descending | Select-Object -First 1
$highHandleProcesses = $processes | Where-Object { $_.Handles -gt 10000 } | Sort-Object Handles -Descending
if ($highHandleProcesses.Count -gt 0) {
$report.Add("[3b] 嚴重警告:發現進程句柄洩漏!")
foreach ($proc in $highHandleProcesses) {
$report.Add(" 🚨 $($proc.Name) 句柄數高達 $($proc.Handles) (正常值 < 5000)")
}
$report.Add(" 這可能導致系統資源耗盡,建議採取以下措施:")
$report.Add(" 1. 立即重開機釋放被佔用的句柄")
$report.Add(" 2. 更新或移除對應的驅動程式/軟體")
$report.Add(" 3. 如果是印表機驅動(jcprinter),請更新或解除安裝")
$report.Add("")
} elseif ($topHandle.Handles -gt 5000) {
$report.Add("[3c] 注意:發現句柄數偏高")
$report.Add(" $($topHandle.Name) 句柄數為 $($topHandle.Handles) (正常值 < 5000)")
$report.Add(" 建議重開機或檢查是否有程式記憶體洩漏")
$report.Add("")
}
# ========== 9. 修改中頁面異常診斷 ==========
if ($modifiedMB -gt 10000) {
$report.Add("[3d] 警告:修改中頁面過高 ($modifiedMB MB)")
$report.Add(" 這表示有大量記憶體正在等待寫入分頁檔")
$report.Add(" 建議增加分頁檔大小或檢查磁碟IO效能")
$report.Add("")
}
# ========== 10. 記憶體趨勢警告 ==========
if ($uptime.TotalHours -gt 48 -and $nonPagedMB -gt 1500) {
$report.Add("[8] 系統已運行超過 2 天且非分頁池過高 -> 強烈懷疑驅動程式洩漏!")
$report.Add("")
}
# ========== 寫入檔案 ==========
$report | Out-File -FilePath $reportPath -Encoding utf8
$processes | Sort-Object WorkingSet_MB -Descending | ForEach-Object {
"$($_.Name.PadRight(25)) | $($_.Count.ToString().PadLeft(4)) | $($_.WorkingSet_MB.ToString().PadLeft(8)) MB | $($_.Private_MB.ToString().PadLeft(8)) MB | $($_.Handles)"
} | Out-File -FilePath $summaryPath -Encoding utf8
Write-Host "分析完成。"
} catch {
Write-Host "錯誤: $($_.Exception.Message)"
}
🚀 如何使用?
- 將上述代碼另存為
Memory_Diagnosis.ps1。 - 在該檔案上點擊右鍵,選擇「使用 PowerShell 執行」。
- 腳本會自動請求權限,並在幾秒內掃描全機狀態。
- 執行完成後,請查閱同資料夾下的報告檔案。
💬 結語
在技術服務的領域,數據就是真相。忠碁科技有限公司 始終致力於透過自動化與精準診斷,協助客戶排除最棘手的系統瓶頸。
這支腳本就像是電腦的 X 光機,能幫你把記憶體黑洞照得一清二楚。如果你也深受「記憶體莫名失蹤」所苦,這份邏輯精鍊而成的工具,絕對是你最好的幫手!
💡 小提醒:若診斷報告顯示 [3b] 嚴重警告:發現進程句柄洩漏,且來源是印表機相關驅動,請務必按照報告中的建議更新或重新安裝驅動程式。
📍 遺珠推薦 (很多人錯過了這篇...):
這封郵件似乎很危險
這封郵件似乎很危險
【免責聲明】 AI-Assisted Content | 部分資料引用自網路、AI的文筆,以及作者的整理。
如有侵權、雷同或標註錯誤,敬請來信明告知,還原始末釐前因。一經確認即撤稿,守護清名謝君勉。
【請認明】🔗 chungg.com 才是官方唯一授權認證網域。
專業領航|知識技術無價 價值實踐|應用服務有價
本頁連結(274369):電腦越用越慢?分享 PowerShell 腳本,一鍵偵測記憶體與句柄洩漏
