Skip to content

Instantly share code, notes, and snippets.

@CTimmerman
Last active May 16, 2024 04:45
Show Gist options
  • Save CTimmerman/6d1fc20c3fb61ef0ba3e2d6de2c582ce to your computer and use it in GitHub Desktop.
Save CTimmerman/6d1fc20c3fb61ef0ba3e2d6de2c582ce to your computer and use it in GitHub Desktop.
Out Of Memory (OOM) Killer to save your SSD and other processes.
pylint:
disable:
- line-too-long
- multiple-imports
- pointless-string-statement
- too-many-nested-blocks
"""Out Of Memory (OOM) Killer by Cees Timmerman, 2012-2023."""
import os, subprocess, sys, time # nosec
import psutil # python -m pip install psutil
MIN_BYTES_FREE = 0.36e9 # Windows 10 and/or Chrome appear to fill the disk rather than put free RAM below 20 MB of 12 GB. 128 MB is also hard to hit. 250e6 works. 404e6 for Mint Cinnamon. 550e6 for Windows 10 when MsMpEng is hogging all resources. 1e9 for temporary blackouts with 100% pagefile use by memcompression instead of nonresponsive Chrome with 16 - 1.1 GB RAM.
KILL_UNTIL_BYTES_FREE = 1.4e9 # Kill until
MAX_BYTES_PROCESS = 390e6 # Kills tabs bigger than this. TODO: Don't kill focused tab.
# Lowercase list of process names that don't need confirmation to be killed.
HIT_LIST = [
"procmon64",
"chrome",
"chromium",
"firefox",
"web content",
"isolated web co",
"microsoftedgecp",
"msedge",
"msmpeng",
"node",
"python",
"python3",
]
def kill(pid):
if os.name == "nt":
print(
subprocess.check_output(["TASKKILL", "/PID", str(pid), "/T", "/F"]) # nosec
) # /Tree /Force. /IM image didn't work though all child processes were named the same.
else:
os.kill(
pid, 15
) # 15 is SIGTERM according to https://en.wikipedia.org/wiki/Signal_(IPC) which is friendlier than SIGKILL (9). On Windows this is just the exit code for the process.
def main(verbose=True):
lines = __doc__.splitlines()
print(lines[0], lines[-1])
print(
f"Watching {HIT_LIST}\nIf free RAM < {MIN_BYTES_FREE/1e9:,.2f} GB, kill until {KILL_UNTIL_BYTES_FREE/1e9:,.2f} GB free."
)
while True:
time.sleep(4)
try:
ram_free = psutil.virtual_memory().available
if verbose or ram_free < MIN_BYTES_FREE:
print(f"\n{time.ctime()} {ram_free / 1e9:,.2f} GB free:")
if psutil.disk_usage('/').free < 1e9:
print("WARNING: Less than 1 GB HDD free. Check downloads.")
proc_data = [
(
p.memory_info().rss,
p.pid,
p.name().split(".")[0].lower(),
p.parent() and p.parent().name().split(".")[0].lower() or None,
)
for p in psutil.process_iter()
]
proc_data.sort(reverse=True)
print(
" ".join(f"{p[2]} {p[0] / 1e6:,.2f} MB" for p in proc_data[:5])
)
if ram_free >= MIN_BYTES_FREE:
continue
if "pcdrmemory" in " ".join(p[2] for p in proc_data):
print("Allowing pcdrmemory test.")
continue
skipped = []
for rss, pid, name, parent in proc_data:
if (
# rss > MAX_BYTES_PROCESS and
ram_free < KILL_UNTIL_BYTES_FREE
and name in HIT_LIST
):
if parent != name and name not in skipped: # and name in ("chrome", "firefox", "msedge"):
print("Skipping main", name)
skipped.append(name)
continue
print(
f"\7Killing {rss/1e6:,.2f} MB {name} {pid} of {parent} on {time.ctime()}"
)
kill(pid)
time.sleep(4)
ram_free = psutil.virtual_memory().available
print(f"Free RAM: {ram_free / 1e9:,.2f} GB")
except Exception as ex:
print(type(ex).__name__, ex, f"on line {sys.exc_info()[2].tb_lineno}")
if __name__ == "__main__":
try:
main("verbose" in sys.argv)
except KeyboardInterrupt:
pass # Normal user exit by Ctrl+Break or Ctrl+C.
[tool.pylint.messages_control]
disable = [
"bad-continuation",
"broad-except",
"invalid-name",
"line-too-long",
"missing-function-docstring",
"mixed-indentation",
"multiple-imports",
"multiple-statements",
"pointless-string-statement",
"too-many-nested-blocks",
]
[bandit]
skips = B101,B311,B404,B603,B607
[flake8]
ignore = E266,E401,E402,E501,E701,W503,W191
[mypy]
ignore_missing_imports = True
ignore_missing_imports_per_module = True
[pycodestyle]
ignore = E265,E266,E401,E402,E501,E701,W191,W503,W504
[pydocstyle]
ignore = D100,D103,D105,D107,D203,D213,D400,D415
[pylama]
ignore = C901
@CTimmerman
Copy link
Author

On Windows 10, you can open C:\Users\[username]\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup by entering shell:startup after pressing Win+R. Put a .bat file there containing python.exe -u "C:\Users\[username]\Documents\code\OOM_killer\OOM_killer.py" to run it on startup.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment