Introduction

PHP OPcache stores precompiled script bytecode in shared memory to avoid parsing and compiling PHP files on every request. When the OPcache memory is full, PHP stops caching new files and existing cached entries may be evicted, causing a significant performance degradation. In production, this manifests as gradually increasing response times, high CPU usage from repeated file compilation, and in extreme cases, request timeouts. The OPcache fills up when opcache.memory_consumption is too small, when there are too many files, or when cache invalidation (file changes) causes constant recompilation.

Symptoms

OPcache status shows memory full:

php
$status = opcache_get_status();
echo "Used: {$status['memory_usage']['used_memory']}\n";
echo "Free: {$status['memory_usage']['free_memory']}\n";
echo "Wasted: {$status['memory_usage']['wasted_memory']}\n";
// Used: 134217728, Free: 0, Wasted: 0  <-- 100% used!

Performance degradation:

```bash # Before OPcache full $ curl -w "%{time_total}s" http://myapp.local/ -o /dev/null 0.045s

# After OPcache full $ curl -w "%{time_total}s" http://myapp.local/ -o /dev/null 0.312s ```

PHP-FPM error log:

bash
[15-Mar-2024 10:23:45] WARNING: [pool www] child 12345 said into stderr:
"NOTICE: PHP message: OPcache memory full, scripts not cached"

Common Causes

  • opcache.memory_consumption too small: Default 128MB is insufficient for large applications (Laravel, Symfony with many packages)
  • opcache.max_accelerated_files too low: Default 10,000 may not cover all files in a project with vendor dependencies
  • Frequent file changes in production: Deployment process that touches files causes cache invalidation
  • Wasted memory not reclaimed: Deleted files still occupy cache space until restart
  • Too many PHP-FPM children: Each child process does not have its own OPcache (it is shared), but restarts fragment the cache
  • Large vendor directory: Composer dependencies add thousands of files to the cache

Step-by-Step Fix

Step 1: Increase OPcache memory allocation

ini
; /etc/php/8.1/fpm/conf.d/10-opcache.ini
opcache.enable=1
opcache.memory_consumption=256       ; Increase from 128 to 256 MB
opcache.max_accelerated_files=20000  ; Increase from 10000
opcache.validate_timestamps=0        ; Production: do not check for file changes
opcache.revalidate_freq=0
opcache.fast_shutdown=1
opcache.enable_cli=0

Step 2: Count files to determine proper max_accelerated_files

```bash # Count all PHP files in your project find /var/www/myapp -name "*.php" | wc -l # Output: 15234

# Set max_accelerated_files to next power of 2 above the count # 15234 -> use 16384 or round up to 20000 for headroom ```

Step 3: Monitor OPcache usage

```php // opcache-monitor.php (restrict access in production!) $status = opcache_get_status(false); $memory = $status['memory_usage']; $total = $memory['used_memory'] + $memory['free_memory'] + $memory['wasted_memory']; $usagePercent = round(($memory['used_memory'] / $total) * 100, 1);

echo "OPcache Usage: {$usagePercent}%\n"; echo "Scripts cached: {$status['opcache_statistics']['num_cached_scripts']}\n"; echo "Hits: {$status['opcache_statistics']['hits']}\n"; echo "Misses: {$status['opcache_statistics']['misses']}\n"; echo "Hit rate: {$status['opcache_statistics']['hit_rate']}%\n";

if ($usagePercent > 90) { echo "WARNING: OPcache nearly full! Increase opcache.memory_consumption.\n"; } ```

Step 4: Reset OPcache after deployment

```bash #!/bin/bash # deploy.sh # ... deploy code ...

# Restart PHP-FPM to clear OPcache sudo systemctl restart php8.1-fpm

# Or use opcache_reset() if graceful restart is preferred # php -r 'opcache_reset();' ```

Prevention

  • Set opcache.validate_timestamps=0 in production to prevent file change checking
  • Size opcache.memory_consumption based on actual file count, not defaults
  • Set opcache.max_accelerated_files to the next prime number above your file count
  • Monitor OPcache hit rate -- anything below 95% indicates cache pressure
  • Use opcache.jit=tracing (PHP 8.0+) for additional performance gains on CPU-bound workloads
  • Add OPcache monitoring to your APM dashboard with alerts at 80% memory usage