I've been having trouble where TTFB would increase after a few hours. I initially thought it was due to something in IIS, but it turns out it's actually opcache.
I also noticed the "manual restarts" value keeps increasing. No manual restarts are initiated, and I verified no such call is present in our PHP scripts.
Configuration:
- Windows Server 2022 (same behavior was present on Server 2016 too)
- IIS 10
- PHP 8.1.4
- 96GB RAM
After a few hours, opcache places this in its error log:
- Warning Not enough free shared space to allocate 4176 bytes (1856 bytes free)
Output of opcache_get_status when site is slow/not using opcache:
["opcache_enabled"]=> bool(true) ["cache_full"]=> bool(true) ["restart_pending"]=> bool(false) ["restart_in_progress"]=> bool(false) ["memory_usage"]=> array(4) { ["used_memory"]=> int(536869056) ["free_memory"]=> int(1856) ["wasted_memory"]=> int(0) ["current_wasted_percentage"]=> float(0) } ["interned_strings_usage"]=> array(4) { ["buffer_size"]=> int(50331160) ["used_memory"]=> int(3990280) ["free_memory"]=> int(46340880) ["number_of_strings"]=> int(53057) } ["opcache_statistics"]=> array(13) { ["num_cached_scripts"]=> int(0) ["num_cached_keys"]=> int(0) ["max_cached_keys"]=> int(65407) ["hits"]=> int(0) ["start_time"]=> int(1648533458) ["last_restart_time"]=> int(1648578530) ["oom_restarts"]=> int(0) ["hash_restarts"]=> int(0) ["manual_restarts"]=> int(232) ["misses"]=> int(11740) ["blacklist_misses"]=> int(0) ["blacklist_miss_ratio"]=> float(0) ["opcache_hit_rate"]=> float(0) } ["scripts"]=> array(0) { } ["jit"]=> array(7) { ["enabled"]=> bool(false) ["on"]=> bool(false) ["kind"]=> int(5) ["opt_level"]=> int(4) ["opt_flags"]=> int(6) ["buffer_size"]=> int(0) ["buffer_free"]=> int(0) }
On another website, the output is similar, except it's showing opcache is now disabled, and "restart_pending" is true, but never actually restarts.
["opcache_enabled"]=> bool(false) ["cache_full"]=> bool(false) ["restart_pending"]=> bool(true) ["restart_in_progress"]=> bool(false) ["memory_usage"]=> array(4) { ["used_memory"]=> int(284053552) ["free_memory"]=> int(252817360) ["wasted_memory"]=> int(0) ["current_wasted_percentage"]=> float(0) } ["interned_strings_usage"]=> array(4) { ["buffer_size"]=> int(50331160) ["used_memory"]=> int(3564656) ["free_memory"]=> int(46766504) ["number_of_strings"]=> int(53844) } ["opcache_statistics"]=> array(13) { ["num_cached_scripts"]=> int(724) ["num_cached_keys"]=> int(1353) ["max_cached_keys"]=> int(65407) ["hits"]=> int(134503) ["start_time"]=> int(1648551278) ["last_restart_time"]=> int(1648557365) ["oom_restarts"]=> int(0) ["hash_restarts"]=> int(0) ["manual_restarts"]=> int(20) ["misses"]=> int(1458) ["blacklist_misses"]=> int(0) ["blacklist_miss_ratio"]=> float(0) ["opcache_hit_rate"]=> float(98.9276336596524) } ["jit"]=> array(7) { ["enabled"]=> bool(false) ["on"]=> bool(false) ["kind"]=> int(5) ["opt_level"]=> int(4) ["opt_flags"]=> int(6) ["buffer_size"]=> int(0) ["buffer_free"]=> int(0) }
Opcache settings (ignore the debug logging mode - just turned that on now to see what it's doing when it fails)
opcache.enable=1
opcache.enable_cli=1
opcache.memory_consumption=512
opcache.interned_strings_buffer=64
opcache.max_accelerated_files=50000
opcache.max_wasted_percentage=15
opcache.use_cwd=1
opcache.validate_timestamps=0
opcache.revalidate_freq=2
opcache.save_comments=1
opcache.error_log=opcache.log
opcache.log_verbosity_level=4
opcache.file_cache_fallback=0
opcache.file_update_protection=0