Ngăn chặn OOM Killer trên Linux làm sập ứng dụng của bạn

intermediate🐧 Linux2026-04-11| Linux (Ubuntu, CentOS, Debian, RHEL), Cloud Instances (AWS, GCP, DigitalOcean), Docker Containers

Error Message

Out of memory: Kill process 4821 (java) score 512 or sacrifice child Killed process 4821 (java) total-vm:2048000kB, anon-rss:1024000kB
#oom#bộ nhớ#kernel#oom-killer#linux

Giải phẫu một sự cố sụp đổ

Máy chủ production của bạn đang hoạt động trơn tru thì đột nhiên mọi thứ tối sầm lại. Không có cảnh báo, không có quá trình tắt máy an toàn—chỉ là một tiến trình bị "chết". Khi bạn kiểm tra log kernel qua lệnh dmesg, bạn thấy dòng log tai tiếng này:

Out of memory: Kill process 4821 (java) score 512 or sacrifice child
Killed process 4821 (java) total-vm:2048000kB, anon-rss:1024000kB

Đây là cơ chế OOM (Out Of Memory) Killer của Linux Kernel đang hoạt động. Nó là một cơ chế an toàn tàn nhẫn. Khi RAM vật lý bị cạn kiệt hoàn toàn, kernel phải lựa chọn: để toàn bộ hệ thống bị treo, hoặc tiêu diệt một tiến trình duy nhất để thu hồi bộ nhớ. Nó luôn chọn phương án thứ hai.

Danh sách "đen": Cách Kernel chọn nạn nhân

Linux sử dụng một hệ thống chấm điểm để quyết định tiến trình nào sẽ bị loại bỏ. Mọi tác vụ đang chạy đều có một oom_score. Các tiến trình có điểm cao hơn sẽ bị nhắm mục tiêu trước. Thuật toán thường săn tìm các tiến trình tiêu thụ lượng RAM khổng lồ nhưng không quan trọng đối với sự ổn định cốt lõi của hệ thống.

Các ứng dụng Java thường xuyên là nạn nhân trên các máy chủ cloud cấu hình thấp. Ví dụ, nếu bạn đang chạy một VPS giá $10/tháng với 2GB RAM, một JVM được cấu hình với heap 1.5GB sẽ khiến hệ điều hành hầu như không còn không gian để "thở". Một khi giới hạn 2GB đó bị chạm tới, kernel nhìn thấy một tiến trình Java đồ sộ và bóp cò.

Bước 1: Xác nhận sự kiện OOM

Đừng đoán mò. Hãy xác minh xem OOM Killer có thực sự là thủ phạm hay không bằng cách tìm kiếm dấu vết "killed" trong log hệ thống:

# Kiểm tra bộ đệm kernel cho các sự kiện gần đây
dmesg -T | grep -i oom

# Tìm kiếm log lịch sử trên Ubuntu/Debian
grep -i 'killed process' /var/log/syslog

# Tìm kiếm log lịch sử trên CentOS/RHEL
grep -i 'killed process' /var/log/messages

Bước 2: Các biện pháp khắc phục ngay lập tức

Phương pháp A: Triển khai lưới an toàn Swap

Nhiều nhà cung cấp cloud hiện nay (như AWS hoặc DigitalOcean) cung cấp các instance không có không gian swap. RAM thì nhanh, nhưng nó có hạn. Swap đóng vai trò như một bình chứa tràn. Nó sẽ không làm ứng dụng của bạn nhanh hơn, nhưng nó sẽ ngăn OOM Killer kích hoạt ngay giây phút mức sử dụng RAM của bạn chạm ngưỡng 100%.

Thực hiện các bước sau để tạo file swap 2GB ngay lập tức:

# Tạo một file 2GB
sudo fallocate -l 2G /swapfile

# Bảo mật quyền hạn file
sudo chmod 600 /swapfile

# Khởi tạo vùng swap
sudo mkswap /swapfile

# Bật swap
sudo swapon /swapfile

# Đảm bảo nó vẫn tồn tại sau khi khởi động lại
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab

Phương pháp B: Điều chỉnh giới hạn Java Heap hợp lý

Nếu Java là nạn nhân của bạn, thiết lập -Xmx (Max Heap) của bạn có lẽ quá cao. Trên một máy chủ có 4GB RAM, việc đặt -Xmx4g là một sai lầm. JVM cần thêm bộ nhớ cho thread stacks và native code, và hệ điều hành cần ít nhất 512MB để hoạt động ổn định.

Điều chỉnh script khởi động của bạn để chừa lại một khoảng trống:

# Ví dụ: Trên máy chủ 2GB RAM, giới hạn heap ở mức 1.2GB
java -Xms512m -Xmx1280m -jar app.jar

Bước 3: Tinh chỉnh nâng cao

Phương pháp C: Bảo vệ các dịch vụ thiết yếu

Đôi khi bạn có một tiến trình quan trọng—như cơ sở dữ liệu hoặc agent giám sát—cần phải duy trì hoạt động bằng mọi giá. Bạn có thể hạ thấp mức độ ưu tiên của nó trong danh sách "đen" của OOM một cách thủ công. Điểm số dao động từ -1000 đến 1000. Việc đặt giá trị -1000 về cơ bản sẽ làm cho tiến trình đó trở nên "không thể bị tiêu diệt".

# Lấy PID của dịch vụ quan trọng
pidof my_database_app

# Thiết lập điều chỉnh (ví dụ: PID 1234)
echo -1000 > /proc/1234/oom_score_adj

Hãy thận trọng khi sử dụng tính năng này. Nếu kernel không thể tiêu diệt tiến trình lớn nhất của bạn, nó sẽ bắt đầu tiêu diệt mọi tiến trình nhỏ hơn khác cho đến khi hệ thống cuối cùng bị treo.

Phương pháp D: Chính sách Overcommit nghiêm ngặt hơn

Theo mặc định, Linux rất lạc quan. Nó cho phép các tiến trình yêu cầu nhiều bộ nhớ hơn mức thực tế hiện có, với giả định rằng chúng sẽ không sử dụng hết cùng một lúc. Bạn có thể buộc kernel thực tế hơn bằng cách thay đổi hành vi overcommit.

# Đặt overcommit thành 'Không overcommit' (Chế độ 2)
sudo sysctl -w vm.overcommit_memory=2
sudo sysctl -w vm.overcommit_ratio=80

Điều này giúp việc cấp phát bộ nhớ trở nên dễ dự đoán hơn nhưng có thể khiến các ứng dụng thất bại với lỗi 'Out of Memory' ngay khi gọi hàm malloc() thay vì bị kernel tiêu diệt sau đó.

Giám sát chủ động

Mục tiêu là khắc phục các vấn đề về bộ nhớ trước khi kernel làm điều đó thay bạn. Hãy giám sát tài nguyên của bạn liên tục:

  • Cảnh báo: Kích hoạt thông báo Slack hoặc email khi mức sử dụng RAM vượt quá 90% trong hơn 5 phút.
  • Phát hiện rò rỉ: Sử dụng top hoặc htop để theo dõi sự tăng trưởng bộ nhớ. Nếu RSS (Resident Set Size) của một ứng dụng tăng lên hàng ngày mà không bao giờ giảm xuống, bạn đang gặp vấn đề rò rỉ bộ nhớ (memory leak).
  • Cô lập Docker: Nếu bạn chạy container, hãy luôn đặt giới hạn cứng (hard limits) trong file docker-compose.yml của mình. Điều này ngăn một container bị rò rỉ bộ nhớ kéo sập toàn bộ máy chủ host.

Xác minh

Sau khi bạn đã thêm swap hoặc điều chỉnh heap, hãy xác minh các giới hạn mới:

# Kiểm tra tổng lượng bộ nhớ và swap đang hoạt động
free -h

# Theo dõi mức sử dụng bộ nhớ trong thời gian thực
watch -n 5 free -h

Theo dõi log của bạn trong 48 giờ tới. Nếu đầu ra của dmesg vẫn sạch sẽ, máy chủ của bạn đã có đủ không gian để duy trì sự ổn định.

Related Error Notes