PHP OPcache и JIT: полная настройка
OPcache кэширует скомпилированный байт-код PHP, устраняя повторную компиляцию при каждом запросе. JIT (Just-In-Time, доступен с PHP 8.0) компилирует байт-код в машинный код — ускорение до 3x для CPU-интенсивных задач.
Базовая настройка OPcache
[opcache]
opcache.enable=1
opcache.enable_cli=0
; Память под кэш байт-кода (мб)
opcache.memory_consumption=256
; Память под строки (мб)
opcache.interned_strings_buffer=16
; Максимум файлов в кэше
opcache.max_accelerated_files=20000
; Проверка изменений файлов (0 = никогда, продакшн)
opcache.validate_timestamps=0
; Перекомпиляция при изменении (dev: 1, prod: 0)
opcache.revalidate_freq=0
; Сохранять комментарии (нужно для Doctrine/аннотаций)
opcache.save_comments=1Настройка JIT (PHP 8.0+)
; JIT буфер
opcache.jit_buffer_size=128M
; Режимы JIT:
; 0 = отключён
; tracing (1255) — лучший для веб-приложений
; function (1205) — для CLI/скриптов
opcache.jit=tracingПрофили для разных окружений
; --- PRODUCTION ---
opcache.validate_timestamps=0
opcache.revalidate_freq=0
opcache.jit=tracing
opcache.jit_buffer_size=128M
; --- DEVELOPMENT ---
opcache.validate_timestamps=1
opcache.revalidate_freq=0 ; проверять при каждом запросе
opcache.jit=0 ; JIT мешает xdebugМониторинг состояния OPcache
<?php
$status = opcache_get_status();
$config = opcache_get_configuration();
echo 'Hit rate: ' . round($status['opcache_statistics']['opcache_hit_rate'], 2) . '%';
echo 'Used memory: ' . round($status['memory_usage']['used_memory'] / 1024 / 1024, 1) . ' MB';
echo 'Cached scripts: ' . $status['opcache_statistics']['num_cached_scripts'];Hit rate выше 95% — хороший показатель. Ниже 80% — увеличьте opcache.memory_consumption.
Сброс кэша при деплое
# Через PHP-CLI
php -r "opcache_reset();"
# Через nginx + PHP-FPM: reload воркеров
php-fpm8.3 -t && systemctl reload php8.3-fpmРеальные ошибки JIT и OPcache
Не удаётся выделить буфер JIT
PHP Warning: Zend OPcache: Failed to create JIT buffer: mmap() failed [12]: Cannot allocate memory in Unknown on line 0Значение opcache.jit_buffer_size превышает то, что реально может выделить процесс — на VPS с малым объёмом RAM или внутри контейнера с жёстким лимитом cgroup. Иногда причина не в объёме памяти, а в политиках безопасности ядра (SELinux/grsecurity), запрещающих исполняемые страницы памяти для JIT — тогда стоит смотреть dmesg на предмет отказов.
JIT отключается при подключении Xdebug
php -v
PHP 8.2.18 (cli) (built: ...)
with Zend OPcache v8.2.18, Copyright (c), by Zend Technologies
with Xdebug v3.3.1, Copyright (c) 2002-2024, by Derick RethansЕсли в этом выводе есть Xdebug, JIT фактически не работает, даже если opcache.jit=tracing стоит в конфиге — расширения несовместимы для одновременной работы. На проде Xdebug быть не должно в принципе, но это частая причина «JIT не ускоряет ничего» при тестировании конфига на staging-сервере с включённым отладчиком.
Когда JIT не подходит
- Обычные CRUD-приложения на WordPress и большинство Laravel-проектов упираются в I/O (базу данных, диск, сеть), а не в CPU — JIT там даёт единицы процентов или ничего.
- Общий shared-хостинг с ограничением памяти на процесс — дополнительные 64–256 МБ под
jit_buffer_sizeмогут просто не влезть в лимит. - Код с активным кэшем байт-кода, но с включённым Xdebug для отладки — JIT в такой конфигурации бесполезен, отключайте его на dev-окружениях явно.
Проверка результата
| Что проверить | Команда |
|---|---|
| JIT включён и активен | php -r "var_dump(opcache_get_status()['jit']['enabled']);" |
| Реальный размер буфера JIT | php -i | grep jit_buffer_size |
| Есть ли предупреждения при старте FPM | php-fpm8.3 -tt 2>&1 | grep -i jit |
Флаг -tt у php-fpm выводит расширенный тест конфигурации со всеми предупреждениями, включая проблемы загрузки модулей — обычный -t показывает только фатальные ошибки синтаксиса.
Перед включением JIT на боевом сервере протестируйте конкретную нагрузку на staging-копии: замерьте время ответа ключевых страниц до и после через ab или wrk. Если разница укладывается в погрешность измерений — оставьте JIT выключенным, лишний код в проде без пользы увеличивает поверхность для редких, трудно воспроизводимых багов самого JIT-компилятора.
На практике для типичного интернет-магазина или блога разница между opcache.jit=tracing и полностью выключенным JIT оказывается в пределах статистической погрешности — весь прирост даёт уже сам OPcache без JIT.