Lỗi Gặp Phải
App của bạn vừa chết với thông báo sau trong logs:
Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 65536 bytes) in /var/www/html/app/vendor/something/heavy.php on line 342
134217728 bytes = 128MB. PHP đã chạm giới hạn bộ nhớ và từ chối cấp phát thêm. Tiến trình đã chết. Người dùng đang thấy trang trắng hoặc lỗi 500.
Kiểm Tra Nhanh
Đầu tiên, xác nhận giới hạn bộ nhớ thực tế PHP đang dùng:
php -r "echo ini_get('memory_limit');"
Hoặc đặt đoạn này vào một file PHP tạm thời có thể truy cập qua trình duyệt:
<?php echo ini_get('memory_limit');
Bạn sẽ thấy 128M, 256M, hoặc bất kỳ giá trị nào đang được giới hạn. Nếu thấy -1, bộ nhớ đang không giới hạn — lỗi xuất phát từ chỗ khác (hãy kiểm tra open_basedir hoặc giới hạn cấp OS).
Cũng cần kiểm tra file php.ini nào đang thực sự được nạp — thường có nhiều hơn một:
php --ini
Riêng với PHP-FPM:
php-fpm8.1 --ini
Giải Pháp 1: Tăng Giới Hạn Trong php.ini (Sửa Vĩnh Viễn)
Bắt đầu từ đây. Thay đổi này có hiệu lực lâu dài — tồn tại qua các lần khởi động lại và áp dụng toàn cục cho tất cả những gì PHP chạy trên server. Tìm và chỉnh sửa file php.ini đang hoạt động:
; Tìm dòng memory_limit và thay đổi:
memory_limit = 512M
Sau khi lưu, khởi động lại PHP-FPM hoặc Apache:
# PHP-FPM
sudo systemctl restart php8.1-fpm
# Apache với mod_php
sudo systemctl restart apache2
Xác nhận thay đổi đã có hiệu lực:
php -r "echo ini_get('memory_limit');"
Giải Pháp 2: Đặt Theo Từng Project (Khi Không Thể Sửa php.ini)
Trên shared hosting hoặc server bị khóa, bạn thường không có quyền truy cập vào php.ini. Một vài cách giải quyết:
Trong .htaccess (Chỉ với Apache)
php_value memory_limit 512M
Trong script PHP của bạn (đầu file)
<?php
ini_set('memory_limit', '512M');
Cách này hoạt động trên hầu hết các host. Một số host vô hiệu hóa ini_set vì lý do bảo mật, hoặc chạy suhosin để chặn hoàn toàn các thay đổi runtime. Nếu cả hai cách đều không hoạt động, bạn cần liên hệ nhà cung cấp hosting.
Dành riêng cho WordPress
Thêm vào wp-config.php trước dòng / That's all, stop editing! /:
define('WP_MEMORY_LIMIT', '512M');
define('WP_MAX_MEMORY_LIMIT', '512M'); // Cho khu vực admin
Giải Pháp 3: Cấu Hình PHP-FPM Pool
Đang chạy PHP-FPM? Cấu hình pool có thể ghi đè hoàn toàn php.ini. Kiểm tra file pool của bạn (thường ở /etc/php/8.1/fpm/pool.d/www.conf):
; Thêm hoặc cập nhật dòng này trong cấu hình pool:
php_admin_value[memory_limit] = 512M
Sau đó khởi động lại:
sudo systemctl restart php8.1-fpm
Directive php_admin_value có độ ưu tiên cao hơn php.ini và không thể bị ghi đè bởi ini_set() trong code ứng dụng — hữu ích khi bạn muốn khóa giới hạn theo từng site.
Xác Nhận Đã Sửa Xong
Đừng giả định rằng việc khởi động lại đã có tác dụng. Hãy xác nhận lại:
# CLI
php -r "echo ini_get('memory_limit') . PHP_EOL;"
# Nếu là web app, gọi đến một endpoint test
curl -s https://yourapp.com/phpinfo.php | grep memory_limit
Kiểm tra error log để xác nhận lỗi fatal không còn xuất hiện nữa:
sudo tail -f /var/log/php8.1-fpm.log
sudo tail -f /var/log/nginx/error.log
Nếu Lỗi Xuất Hiện Trở Lại?
Tăng giới hạn một lần là bình thường. Nhưng nếu phải tăng vài tháng một lần thì đó là câu chuyện khác — có thứ gì đó trong code đang kéo nhiều dữ liệu vào bộ nhớ hơn mức cần thiết. Một số nguyên nhân phổ biến:
- Query lấy dữ liệu lớn: Nạp hàng nghìn object Eloquent/Doctrine cùng lúc. Hãy dùng chunking:
User::chunk(200, fn($users) => ...) - Xử lý ảnh: GD và Imagick ngốn bộ nhớ rất nhiều. Xử lý một ảnh 20MP có thể tiêu tốn hơn 300MB. Resize trước khi xử lý, hoặc đẩy sang queue worker với giới hạn cao hơn.
- Autoload Composer bị phình to: Quá nhiều package nạp cùng lúc. Đo lường bằng
memory_get_peak_usage(true). - Hàm đệ quy hoặc vòng lặp vô tận: Những thứ này âm thầm ăn hết bộ nhớ. Thêm bộ đếm độ sâu hoặc chuyển sang cách tiếp cận lặp.
Kiểm tra bộ nhớ nhanh:
<?php
// Thêm các điểm kiểm tra xuyên suốt code của bạn
echo memory_get_usage(true) / 1024 / 1024 . ' MB' . PHP_EOL;
// Ở cuối:
echo 'Peak: ' . memory_get_peak_usage(true) / 1024 / 1024 . ' MB' . PHP_EOL;
Nên Đặt Bao Nhiêu Bộ Nhớ?
Tùy thuộc vào stack của bạn. Đây là những giá trị thực tế phù hợp:
- Web app thông thường: 256M là hợp lý
- WordPress với nhiều plugin: 256M–512M
- Laravel/Symfony với tác vụ nặng: 256M–512M
- Xử lý ảnh / import dữ liệu: 512M–1024M cho CLI worker
- Không bao giờ đặt
-1cho web process trên production — một script chạy lỗi sẽ làm sập cả server
Đặt giới hạn cứng và để nó báo lỗi to khi có sự cố. Việc bộ nhớ bị tiêu thụ âm thầm còn tệ hơn một lỗi hiện ra rõ ràng.
Tại Sao 128MB Không Còn Đủ Nữa
Giá trị mặc định đó được đặt từ thời PHP 5.2, khi hầu hết các app chỉ gồm vài script và một hai câu query MySQL. Mọi thứ đã thay đổi. Một app Laravel 11 mới với vài package tiêu chuẩn có thể tiêu tốn 60–80MB chỉ trong quá trình khởi động — trước khi xử lý bất kỳ request thực nào.
Tăng giới hạn một lần là quyết định đúng đắn. Nhưng nếu bạn phải tăng đi tăng lại nhiều lần, app đang nạp dữ liệu không cần thiết: kết quả toàn bộ bảng được hydrate thành object, các package không dùng đến vẫn bị autoload, ảnh được xử lý inline thay vì đưa vào queue.
Đối với CLI script và queue worker, hãy đặt giới hạn cao hơn trong pool hoặc script riêng của chúng. Đừng tăng giới hạn web toàn cục chỉ để phục vụ một job import chạy ban đêm.

