Zend OPcache: concepto, instalación y configuración, con los valores de php.ini y cómo desactivar JIT
Zend OPcache es una extensión de PHP que compila los scripts de PHP a bytecode y los guarda en memoria compartida, de modo que se omite la compilación cuando se vuelve a ejecutar el mismo archivo. Cuando, tras activar JIT en un servidor de WordPress con PHP 8.3, todas las páginas devolvieron un error 500, se recuperó el servidor dejando OPcache como estaba y desactivando solo JIT.
Desde PHP 5.5, OPcache viene incluido en PHP de forma predeterminada.
1️⃣ Concepto de Zend OPcache: caché de bytecode
Cada vez que llega una solicitud, un script de PHP pasa por tres etapas: leer el archivo, compilarlo a bytecode y ejecutarlo. Con OPcache en funcionamiento, a partir de la segunda solicitud del mismo archivo desaparecen las dos primeras etapas y solo queda la ejecución.
La memoria compartida donde OPcache guarda el bytecode la usan a la vez todos los workers de PHP-FPM. Si hay varios sitios conectados al mismo PHP-FPM, los scripts de esos sitios también se guardan en una única memoria compartida.
Lo que guarda OPcache es código PHP compilado, así que la ejecución de PHP y las consultas a la base de datos se siguen haciendo cada vez que se abre una página. Guardar el HTML ya generado para saltarse por completo la ejecución de PHP es tarea de la caché de páginas.
2️⃣ Comprobar la instalación de PHP OPcache
Para saber si OPcache está instalado, mira la última línea de la salida de [php -v].
php -vPHP 8.3.22 (cli) (built: Jun 3 2025 17:17:57) (NTS gcc x86_64)
Copyright (c) The PHP Group
Zend Engine v4.3.22, Copyright (c) Zend Technologies
with Zend OPcache v8.3.22, Copyright (c), by Zend TechnologiesSi falta la última línea, instálalo con dnf. El paquete suele llamarse php-opcache; si instalaste varias versiones de PHP a la vez desde el repositorio Remi, el nombre lleva la versión, como php83-php-opcache.
dnf -y install php-opcacheEl paquete php-opcache del repositorio Remi carga la extensión mediante la línea [zend_extension=opcache] de [/etc/php.d/10-opcache.ini]. Después de instalarlo, reinicia PHP-FPM para que OPcache se aplique también a las solicitudes web.
3️⃣ Configuración de OPcache: valores de php.ini
La configuración de OPcache puede ir en dos sitios: la sección [opcache] de [/etc/php.ini] y [/etc/php.d/10-opcache.ini].
php --iniConfiguration File (php.ini) Path: /etc
Loaded Configuration File: /etc/php.ini
Scan for additional .ini files in: /etc/php.d
Additional .ini files parsed: /etc/php.d/10-opcache.ini,Los archivos de la carpeta php.d se leen después de php.ini, así que, si pones la misma directiva en los dos sitios, se aplica el valor de php.d.
La tabla siguiente muestra los valores predeterminados de PHP 8.3 y los valores aplicados en el servidor de WordPress que se revisó.
| Categoría | Valor predeterminado de PHP 8.3 | Valor aplicado | Significado |
|---|---|---|---|
| opcache.enable | 1 | 1 | Activa OPcache |
| opcache.enable_cli | 0 | 1 | También se usa en PHP de línea de comandos (wp-cli, etc.) |
| opcache.memory_consumption | 128 | 1024 | Memoria compartida que guarda el bytecode (MB) |
| opcache.interned_strings_buffer | 8 | 16 | Memoria que guarda una sola vez las cadenas idénticas (MB) |
| opcache.max_accelerated_files | 10000 | 10000 | Límite máximo de claves que se pueden guardar en caché (el límite real es un número primo igual o superior a este valor: 16229 para 10000) |
| opcache.max_wasted_percentage | 5 | 10 | Si falta memoria y el espacio desperdiciado supera este porcentaje, se vacía la caché y se reinicia |
| opcache.validate_timestamps | 1 | 1 | Comprueba si los archivos han cambiado |
| opcache.revalidate_freq | 2 | 60 | Cada cuánto se comprueban los cambios en los archivos (segundos) |
| opcache.jit | tracing | disable | Modo de JIT (desde PHP 8.4 el valor predeterminado es disable) |
| opcache.jit_buffer_size | 0 | 0 | Memoria para JIT; con 0, JIT no funciona (desde PHP 8.4 el valor predeterminado es 64M) |
Los valores puestos en la sección [opcache] de [/etc/php.ini] son los siguientes. El tamaño de memoria se ajusta a la memoria del servidor y [opcache.max_accelerated_files] al número de archivos de WordPress.
[opcache]
opcache.enable=1
opcache.enable_cli=1
opcache.memory_consumption=1024
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=10000
opcache.max_wasted_percentage=10
opcache.validate_timestamps=1
opcache.revalidate_freq=60Si vuelves a poner una línea [zend_extension] en php.ini, la extensión se carga dos veces y aparece el aviso [Cannot load Zend OPcache].
Con [opcache.revalidate_freq] en 60, tras modificar un archivo PHP puede seguir ejecutándose el código anterior hasta 60 segundos.
En un servidor de WordPress donde los archivos PHP cambian a menudo, deja [opcache.validate_timestamps] en 1 para que se compruebe si los archivos han cambiado. El 0 que la documentación de Symfony recomienda para servidores de producción es un valor adecuado para servidores donde los archivos PHP solo cambian al desplegar una versión nueva.
Aunque [opcache.validate_timestamps] esté en 0, si actualizas desde el panel de administración, WordPress vacía por sí mismo la caché de los archivos modificados. Pero si actualizas con wp-cli o modificas archivos por SSH, se sigue ejecutando el código anterior hasta que reinicies PHP-FPM.
4️⃣ Desactivar JIT de OPcache
El JIT de PHP 8 acelera las tareas que hacen muchos cálculos de CPU, así que su efecto es pequeño en una aplicación como WordPress, que atiende solicitudes web. En la medición de WordPress publicada en el RFC de JIT de PHP, con JIT activado se atendieron 326 solicitudes por segundo y con JIT desactivado, 315.
En un servidor de WordPress con PHP 8.3.22 se activó JIT con [opcache.jit=tracing] y [opcache.jit_buffer_size=128M]. Tres días después, un día en que una avalancha de solicitudes hizo reiniciar varias veces a los workers de PHP-FPM, todas las páginas devolvieron un error 500.
En el registro de errores quedó el mensaje [Allowed memory size of 1073741824 bytes exhausted]. El [tried to allocate 4295229440 bytes] que le sigue es el registro de un intento de asignar 4 GB de una vez con un límite de memoria de PHP de 1 GB. Al desactivar JIT, las mismas solicitudes se procesaron con normalidad.
JIT se desactiva poniendo las dos líneas siguientes en [/etc/php.d/10-opcache.ini]. Aunque queden ajustes de JIT en [/etc/php.ini], se aplican los valores de este archivo.
opcache.jit=disable
opcache.jit_buffer_size=0En PHP 8.3 el valor predeterminado de [opcache.jit_buffer_size] es 0, así que, si no lo configuras aparte, JIT no funciona. Desde PHP 8.4, el valor predeterminado de [opcache.jit] también es disable.
5️⃣ Aplicar y comprobar la configuración
La configuración modificada solo se aplica al reiniciar PHP-FPM.
systemctl restart php-fpmTras reiniciar, la memoria compartida de OPcache se vacía y se vuelve a asignar con la configuración nueva.
php -i | grep -E '^opcache\.(enable|memory_consumption|revalidate_freq|validate_timestamps|jit) 'opcache.enable => On => On
opcache.jit => disable => disable
opcache.memory_consumption => 1024 => 1024
opcache.revalidate_freq => 60 => 60
opcache.validate_timestamps => On => OnLos valores que muestra [php -i] son los de PHP de línea de comandos. En la familia RHEL, PHP-FPM lee los mismos archivos [/etc/php.ini] y [/etc/php.d], así que los valores coinciden.
Si llamas a [opcache_get_status()] desde la línea de comandos, aparecen 0 scripts en caché. PHP de línea de comandos crea una memoria compartida nueva en cada ejecución, así que el uso de PHP-FPM se comprueba subiendo el archivo siguiente a la carpeta de documentos web y abriéndolo en el navegador.
<?php
header('Content-Type: text/plain');
$s = opcache_get_status(false);
print_r($s['memory_usage']);
print_r($s['opcache_statistics']);
print_r($s['interned_strings_usage']);
var_dump($s['cache_full']);Si [cache_full] es true, la memoria compartida o las claves están llenas. Después de comprobarlo, borra este archivo enseguida.
En un servidor donde tres sitios de WordPress usan el mismo PHP-FPM, se estaban usando 317 MB de los 1024 MB de memoria compartida. Había entre 3.366 y 4.222 scripts en caché por sitio, 11.358 en total.
OPcache guarda los scripts con su ruta real y, para los archivos cargados con una ruta relativa, crea una clave más con el directorio de trabajo añadido, así que el número de claves es mayor que el de scripts. Con 11.358 scripts, las claves llenaron el límite de 16.229 y el búfer de cadenas de 16 MB tampoco tenía espacio libre.
Cuando las claves se llenan, los scripts nuevos no se guardan en caché y se compilan en cada solicitud. En 5 minutos y 21 segundos, los fallos de caché pasaron de 18.408 a 36.027, pero el número de scripts en caché siguió en 11.358.
En un servidor con varios WordPress o con muchos plugins, fija [opcache.max_accelerated_files] con holgura para que no se llene el límite de claves. Con 20000, el límite real de claves es de 32.531. Si el búfer de cadenas está lleno, aumenta también [opcache.interned_strings_buffer].
🔢 FAQ & contenido recomendado
Caché
Caché: definición y todos los campos donde se usa
Una guía que clasifica los campos donde se usa la caché, con OPcache, que guarda código PHP compilado, y la caché de páginas, que guarda HTML ya generado.
PHP-FPM
Aviso de php-fpm max_children: encuentra la causa en los logs antes de añadir workers
Una guía para averiguar en los logs la causa del aviso que aparece cuando se llenan los workers de PHP-FPM que comparten la memoria compartida de OPcache.
PHP-FPM
Qué es PHP-FPM | Instalar y configurar PHP-FPM
Una guía sobre la instalación de PHP-FPM, dentro del cual funciona OPcache, la configuración del pool de www.conf y cómo reiniciarlo.
ℹ️ Aviso de afiliados
El contenido de este sitio incluye enlaces de afiliado. Cuando un visitante compra un producto o servicio a través de uno de ellos, el sitio recibe una comisión del vendedor. El importe que paga el comprador(baja durante las promociones ↓)no aumenta. Los precios, descuentos y existencias publicados corresponden al momento de redacción y pueden variar, así que confírmalos con el vendedor antes de comprar. Los productos se eligen y evalúan con criterios propios, y las comisiones no influyen en el orden ni en el contenido de las reseñas.