Lüfter dreht auf? CPU-Fresser unter Windows mit PowerShell finden
Der CPU-Fresser ist der Prozess, der gerade die meiste Rechenzeit verbraucht — nicht der, der seit dem Start die meiste verbraucht hat. Wenn der Lüfter plötzlich aufdreht, belegen meist ein oder mehrere Prozesse dauerhaft Kerne: vergessene Skripte, hängende Hintergrunddienste oder eingefrorene Renderer. Mit drei PowerShell-Schritten findest du heraus, welcher Prozess es ist, wie lange er läuft und wer ihn gestartet hat.
Stand: 16. Oktober 2026. Getestet mit Windows PowerShell 5.1 und PowerShell 7.6 unter Windows 11.
Warum zeigt Get-Process den falschen Prozess?
Weil die Spalte CPU von Get-Process die aufsummierte Rechenzeit seit dem Start des Prozesses in Sekunden enthält, nicht die aktuelle Last. Ein Browser, der seit Tagen läuft, steht deshalb oben, obwohl er gerade nichts tut. Die aktuelle Auslastung in Prozent liefert die WMI-Klasse Win32_PerfFormattedData_PerfProc_Process.
Schritt 1: Wie listest du die CPU-Fresser mit Laufzeit auf?
Mit einer einzigen Abfrage auf Win32_PerfFormattedData_PerfProc_Process: Sie liefert je Prozess die aktuelle Auslastung in Prozent, den privaten Arbeitsspeicher und die Laufzeit in Sekunden. Der Befehl filtert Gesamtsumme und Leerlauf heraus, sortiert absteigend nach Last und zeigt die ersten 15 Prozesse mit der Laufzeit als Tage.Stunden:Minuten:Sekunden.
Get-CimInstance Win32_PerfFormattedData_PerfProc_Process |
Where-Object { $_.Name -notmatch '^(_Total|Idle)$' -and $_.PercentProcessorTime -gt 0 } |
Sort-Object PercentProcessorTime -Descending |
Select-Object -First 15 `
Name,
IDProcess,
@{N="CPU_%"; E={$_.PercentProcessorTime}},
@{N="RAM_MB"; E={[math]::Round($_.WorkingSetPrivate / 1MB, 1)}},
@{N="Laufzeit"; E={[TimeSpan]::FromSeconds($_.ElapsedTime).ToString("dd\.hh\:mm\:ss")}} |
Format-Table -AutoSize
Die Beispielausgabe stammt von einem Entwicklungsrechner (Oktober 2026), gekürzt auf fünf Zeilen:
Name IDProcess CPU_% RAM_MB Laufzeit
---- --------- ----- ------ --------
python#1 46856 100 3,10 00.16:21:03
python#2 24388 97 3,20 00.13:30:18
Code#27 16148 97 410,90 00.15:13:34
IntelGraphicsSoftware.Service 7040 97 41,30 02.22:14:10
svchost#72 11156 72 15,00 02.22:15:09
Beide python-Prozesse laufen hier seit mehr als 13 Stunden und belegen aktuell jeweils einen vollen Kern, bei nur gut 3 MB privatem Speicher.
| Spalte | Bedeutung | Worauf achten? |
|---|---|---|
Name |
Prozessname; #1, #2 trennen mehrere Instanzen |
Hinter mehreren gleichen Namen stecken oft Unterprozesse. |
IDProcess |
Prozess-ID (PID) | Brauchst du für Schritt 2 und Schritt 3. |
CPU_% |
Auslastung bezogen auf einen Kern | 100 entspricht einem vollen Kern; mehr als 100 heißt, dass der Prozess mehrere Kerne nutzt. |
RAM_MB |
privater Arbeitsspeicher | Sehr wenig Speicher bei voller Last kann auf eine Schleife hindeuten, beweist sie aber nicht. |
Laufzeit |
Tage.Stunden:Minuten:Sekunden seit dem Start |
Ein Skript, das seit Stunden einen Kern voll belegt, ist verdächtig — außer es soll genau das tun, etwa ein Build, ein Rendering oder ein Backup. |
Taucht MsMpEng (die Engine von Microsoft Defender) auf einem Datenbankserver dauerhaft ganz oben auf, helfen angepasste Virenschutz-Ausschlüsse (wäge die Sicherheitsfolgen vorher ab) und ein passender Energieplan; beides steht in Windows unter dem Datenbankserver.
Schritt 2: Wie findest du heraus, wer den Prozess gestartet hat?
Über die Klasse Win32_Process: Sie liefert zu jeder Prozess-ID den Startbefehl (CommandLine), den Startzeitpunkt (CreationDate) und die ID des Elternprozesses (ParentProcessId). Damit siehst du, welches Skript läuft und welches Programm es gestartet hat. Die Abfrage funktioniert in Windows PowerShell 5.1 und in PowerShell 7.
# Die PIDs aus Schritt 1 einsetzen
Get-CimInstance Win32_Process -Filter "ProcessId = 46856 OR ProcessId = 24388" |
Select-Object ProcessId, Name, ParentProcessId, CreationDate, CommandLine |
Format-List
Den Elternprozess und dessen Eltern verfolgt diese kleine Funktion bis zur Wurzel (höchstens zehn Ebenen):
function Get-ProcessChain {
param([Parameter(Mandatory)][int]$ProcessId)
for ($i = 0; $i -lt 10 -and $ProcessId -gt 0; $i++) {
$p = Get-CimInstance Win32_Process -Filter "ProcessId = $ProcessId"
if (-not $p) { break }
$p | Select-Object ProcessId, Name, CommandLine
$ProcessId = $p.ParentProcessId
}
}
Get-ProcessChain 46856 | Format-List
Bei einem svchost.exe-Prozess zeigt die Dienstliste, was darin läuft:
Get-CimInstance Win32_Service -Filter "ProcessId = 11156" | Select-Object Name, DisplayName
In PowerShell 7 gibt es eine Kurzform: Get-Process -Id 46856 | Select-Object Id, ProcessName, Parent, CommandLine. Sie liefert in Windows PowerShell 5.1 nur leere Spalten, deshalb steht oben die CIM-Variante.
Schritt 3: Wie beendest du einen hängenden Prozess?
Mit Stop-Process -Id <PID>: Das beendet einen Prozess sofort, ungespeicherte Daten gehen verloren. Beende nur Prozesse, deren Startbefehl du verstanden hast. Probiere den Befehl zuerst mit -WhatIf, er zeigt dann nur das Ziel an. Kindprozesse beendet Windows in der Regel nicht automatisch mit ihrem Elternprozess, sie müssen einzeln oder per Prozessbaum beendet werden.
# Die PID aus Schritt 1 und 2 einsetzen, keine Beispielwerte übernehmen
Stop-Process -Id <PID> -WhatIf # nur anzeigen, was passieren würde
Stop-Process -Id <PID> # beenden
# Hängt der Prozess und reagiert nicht: -Force erzwingt das Beenden
Stop-Process -Id <PID> -Force
# Prozess samt Kindprozessen beenden
taskkill /PID <PID> /T /F
Wie hängende Läufe bei Prozessen aussehen, die wochenlang durchlaufen, zeigt Ollama im Dauerbetrieb: sieben Fehler, die wie Modellfehler aussehen. Auf Datenbankservern ist „welcher Prozess verbraucht die Ressourcen?“ der erste Schritt jeder Performance-Analyse, bevor es um Indexstrategien und Abfragen geht, wie sie das Seminar Datenbank-Performance-Tuning behandelt.
Wie rufst du die Abfrage mit einem Kurzbefehl auf?
Du legst die Abfrage aus Schritt 1 als Funktion in dein PowerShell-Profil und gibst ihr einen Alias, etwa ps-top. Danach genügt der Befehl ps-top in jeder neuen Konsole derselben PowerShell-Version. Windows PowerShell 5.1 und PowerShell 7 haben getrennte Profildateien, du musst die Funktion also je Version eintragen; $PROFILE zeigt jeweils auf die Datei des Hosts, in dem du den Befehl eingibst.
Zuerst legst du die Profildatei an, falls sie fehlt, und öffnest sie:
if (-not (Test-Path $PROFILE)) { New-Item -ItemType File -Path $PROFILE -Force | Out-Null }
notepad $PROFILE
Dann fügst du diese Funktion ein und speicherst die Datei:
function Get-TopProcesses {
param([int]$Top = 15)
Get-CimInstance Win32_PerfFormattedData_PerfProc_Process |
Where-Object { $_.Name -notmatch '^(_Total|Idle)$' -and $_.PercentProcessorTime -gt 0 } |
Sort-Object PercentProcessorTime -Descending |
Select-Object -First $Top `
Name,
IDProcess,
@{N="CPU_%"; E={$_.PercentProcessorTime}},
@{N="RAM_MB"; E={[math]::Round($_.WorkingSetPrivate / 1MB, 1)}},
@{N="Laufzeit"; E={[TimeSpan]::FromSeconds($_.ElapsedTime).ToString("dd\.hh\:mm\:ss")}} |
Format-Table -AutoSize
}
Set-Alias -Name ps-top -Value Get-TopProcesses
Ab dem nächsten Terminal-Start zeigt ps-top die Top 15 und ps-top 5 die Top 5.
Häufige Fragen
Was bedeutet ein CPU-Wert über 100 Prozent?
Der Wert bezieht sich auf einen Kern: 100 entspricht einem voll ausgelasteten Kern. Nutzt ein Prozess mehrere Threads parallel, kann er bis zur Kernzahl mal 100 erreichen. In einer Messung am 16. Oktober 2026 zeigte ein mehrthreadiger Prozess 285 Prozent.
Warum steht System oder svchost ganz oben?
System (PID 4) ist der Windows-Kernel, svchost.exe hostet Dienste. Beides sind Teile von Windows und keine Kandidaten zum Beenden. Mit Get-CimInstance Win32_Service -Filter "ProcessId = <PID>" siehst du, welcher Dienst in einem svchost-Prozess läuft, und kannst dort die Ursache suchen.
Brauche ich Administratorrechte?
Für Auflistung und Beenden eigener Prozesse nicht. Für Systemdienste und Prozesse anderer Benutzer bleibt der Startbefehl ohne Administratorrechte leer, und Beenden schlägt dort ohne erhöhte Rechte fehl. Starte PowerShell in diesem Fall als Administrator.
Läuft das in Windows PowerShell 5.1 und in PowerShell 7?
Ja, die Schritte 1 und 2 laufen in beiden unverändert, weil sie nur CIM-Abfragen nutzen. Die Eigenschaften Parent und CommandLine von Get-Process gibt es nur in PowerShell 7; in 5.1 sind sie leer.
Ab wann ist ein Prozess mit voller Auslastung verdächtig?
Eine feste Grenze gibt es nicht. Build, Rendering und Backup dürfen stundenlang einen Kern belegen. Verdächtig ist ein Prozess, der lange einen Kern auslastet, ohne Ausgabe oder Fortschritt zu zeigen, und dessen Startbefehl du nicht zuordnen kannst. Prüfe vor dem Beenden immer CommandLine und den Elternprozess.
Verwandte Seminare und Artikel
- Windows unter dem Datenbankserver — Windows-Einstellungen unter SQL Server und PostgreSQL: Energieplan, Virenschutz-Ausschlüsse, Dienstrechte
- Ollama im Dauerbetrieb: sieben Fehler, die wie Modellfehler aussehen — und es nicht sind — Fehlerbilder aus dem Dauerbetrieb eines lokalen KI-Servers, je mit Symptom, Ursache und erstem Prüfschritt
- Datenbankübergreifendes Performance-Tuning — Seminar: Indexstrategien, Query-Optimierung und Ausführungspläne
- PostgreSQL Performance-Tuning — Seminar für DBAs und Entwickler — Seminar: Planer, Autovacuum, Indextypen und Konfiguration
Im Seminar vertiefen
Wer die Last nicht nur finden, sondern auf Datenbankservern dauerhaft senken will, findet das passende Seminar zur Performance-Analyse; dazu gibt es auch Beratung und Coaching. Das Angebot richtet sich an Unternehmen, Behörden und Selbstständige. Die Anfrage ist unverbindlich und kostenlos; Umfang und Konditionen klären wir danach individuell.
Inhouse-Seminar: Datenbank-Performance-Tuning
Du arbeitest an Indexstrategien, Query-Optimierung und Ausführungsplänen, herstellerübergreifend für PostgreSQL, MySQL und Oracle. Der Schritt davor ist der aus diesem Artikel: erst klären, welcher Prozess die Last verursacht.
Coaching am Arbeitsplatz
Dauerlast, hängende Prozesse oder ein langsamer Datenbankserver: Wir analysieren das gemeinsam an deinen eigenen Systemen, vor Ort in Köln und Leverkusen sowie bundesweit inhouse oder remote.