OPcache 与 JIT

OPcache 在 OxPHP 中开箱即用。所有 PHP 工作线程共享同一个 OPcache 内存段。脚本在首次执行时编译一次,此后由每个工作进程从缓存中直接提供服务。启用这种共享无需任何特殊配置。

OPcache 在 OxPHP 中的工作方式

OxPHP 将自身注册为一个具名 SAPI,OPcache 对待它的方式与对待其他服务器 SAPI 完全相同。其关键特性包括:

  • 工作进程间共享缓存:所有 PHP 工作线程使用同一份已编译的操作码缓存。一个工作进程编译了某个文件,所有工作进程都能受益。
  • 无需按请求编译:在对每个脚本的首次请求之后,后续请求会完全跳过解析和编译步骤。
  • opcache.enable_cli 对 OxPHP 无效。该设置仅适用于名为 cliphpdbg 的 SAPI。OxPHP 以 SAPI 名称 cli-server 注册,因此 OPcache 仅通过 opcache.enable 控制。如果你在同一个容器中运行 PHP CLI(例如用于迁移或 Artisan 命令),opcache.enable_cli 设置就会派上用场。官方 OxPHP 镜像在服务器二进制文件之外还附带了 PHP CLI,因此如果你的 CLI 脚本能从缓存中获益,可以设置 opcache.enable_cli=1

要启用 OPcache,至少需要:

ini
[opcache] opcache.enable=1
Note

官方 OxPHP Docker 镜像基于 php:*-zts-alpine,它将 OPcache 静态编译进了 PHP 二进制文件。请勿在你的 INI 文件中添加 zend_extension=opcache。该扩展已经加载,添加这一行会导致每次 PHP 启动时都出现一条警告。只需要 [opcache] 配置节即可。

推荐的生产环境设置

以下设置针对 PHP 文件在运行时不会改变的生产环境容器部署进行了优化。禁用时间戳校验并在启动时预加载已编译文件,以获得最大吞吐量。

ini
[opcache] opcache.enable=1 opcache.memory_consumption=128 opcache.interned_strings_buffer=16 opcache.max_accelerated_files=10000 opcache.validate_timestamps=0 opcache.revalidate_freq=0 opcache.file_update_protection=0 opcache.jit_buffer_size=64M opcache.jit=tracing
设置 推荐值 说明
memory_consumption 128 用于存放已编译脚本的共享内存(MB)。如果 opcache_get_status() 显示空闲内存较低,请调高此值。
interned_strings_buffer 16 用于所有工作进程共享的驻留字符串(interned strings)的内存(MB)。
max_accelerated_files 10000 缓存脚本的最大数量。请将其设置得高于你的 .php 文件总数。
validate_timestamps 0 当为 0 时,OPcache 从不检查文件系统的变更。重启容器或调用 opcache_reset() 才能应用代码变更。
revalidate_freq 0 两次文件系统检查之间的间隔秒数。当 validate_timestamps=0 时无效。
file_update_protection 0 文件修改后经过多少秒才有资格被缓存。设为 0 可在启动时立即缓存。

开发环境设置

在开发环境中,启用时间戳校验,使代码变更无需重启容器即可生效。禁用 JIT 以在调试时获得更清晰的堆栈追踪。

ini
[opcache] opcache.enable=1 opcache.memory_consumption=128 opcache.interned_strings_buffer=16 opcache.max_accelerated_files=10000 opcache.validate_timestamps=1 opcache.revalidate_freq=2 opcache.jit_buffer_size=0 opcache.jit=disable

validate_timestamps=1 时,OPcache 每隔 revalidate_freq 秒检查一次文件的修改时间。这会带来少量的按请求开销,但让你能够编辑 PHP 文件并在下一次请求中看到变更。

这是 OxPHP 推荐的开发模式重载策略。 OPcache 在每次 include 时内联执行检查,因此代码修改会在下一次请求时被应用,无需重启容器,也无需外部文件监视守护进程。使用 revalidate_freq=0 可实现每次 include 都立即 stat(精度最高,I/O 略多),或使用 revalidate_freq=2 来分摊 stat 开销。上面展示的默认值是一个不错的平衡,尤其是当你的 DOCUMENT_ROOT 位于慢速的绑定挂载卷上时(macOS/Windows 上的 Docker)。

validate_timestamps 不会重载的内容

即便设置了 validate_timestamps=1,仍有几类变更需要重启容器(或回收工作进程):

  • 预加载文件opcache.preload)在启动时被链接进服务器,永远不会被重新校验。修改了预加载文件——请重启容器。
  • 工作进程模式的引导状态——在工作进程模式中,自动加载器、DI 容器以及任何在外层作用域中构建的对象都驻留在工作进程内存里。OPcache 会重新编译已变更的类文件,但工作进程不会重新运行其引导过程。对于开发环境的工作进程循环,可以在每次请求结束时调用 Worker::scheduleExit()(例如置于 OXPHP_DEV 环境标志之后)来回收工作进程,从而重新执行外层作用域并应用所有变更。
  • 框架级缓存——已编译的 Symfony 容器、Laravel 的路由/配置/视图缓存、Composer 优化后的 classmap。这些都是 OPcache 会重新校验的 .php 文件,但其内部的值引用了过时的类路径或容器 ID。请运行框架的 cache:clear 命令;仅靠 OPcache 是不够的。
  • 非 PHP 文件——.envcomposer.json、YAML/JSON 配置、在 OPcache 之外编译的模板文件。OPcache 只跟踪它编译过的文件;其他一切都需要重启。

JIT 编译

OPcache 的 JIT 编译器在运行时将 PHP 操作码翻译为原生机器码。使用 tracing 模式以获得最佳优化:

ini
opcache.jit=tracing opcache.jit_buffer_size=64M

JIT 对 CPU 密集型的 PHP 代码收益最大:计算密集的循环、字符串处理、图像处理以及模板渲染。对于大部分时间都在等待数据库查询或外部 API 调用的 I/O 密集型应用,提升则微乎其微。

要禁用 JIT:

ini
opcache.jit=disable opcache.jit_buffer_size=0

预加载

OPcache 预加载会在服务器启动时、任何请求被处理之前编译并缓存 PHP 文件。这彻底消除了首次请求的编译开销,并使类和函数在全局可用,无需任何 require 或自动加载开销。

在你的 INI 文件中配置预加载:

ini
opcache.preload=/var/www/html/preload.php opcache.preload_user=www-data

创建一个 preload.php 脚本来加载你最常用的文件:

preload.php
<?php // preload.php — runs once at server startup require __DIR__ . '/vendor/autoload.php'; // Preload framework core files $files = glob(__DIR__ . '/vendor/symfony/http-kernel/**.php'); foreach ($files as $file) { opcache_compile_file($file); } // Preload hot application paths opcache_compile_file(__DIR__ . '/src/Controller/ApiController.php'); opcache_compile_file(__DIR__ . '/src/Service/UserService.php');
Note

预加载的类和函数对所有请求永久可用。不重启服务器就无法更改它们。

工作进程模式与预加载

如果你使用工作进程模式,你的应用已经初始化过一次:自动加载器、配置以及数据库连接会在多次请求之间持续存在。OPcache 预加载通过消除操作码编译开销来对此形成补充,但它并不能替代应用初始化。这两种机制彼此独立,可以一起使用。

应用 PHP 配置

OxPHP 从标准的 conf.d 目录读取 PHP 配置。使用 Docker 卷或 COPY 指令来提供你的自定义 INI 文件。

bash
docker run -p 80:80 \ -v ./custom.ini:/usr/local/etc/php/conf.d/custom.ini:ro \ ghcr.io/oxphp/oxphp:0.10.0

监控缓存状态

从 PHP 中检查实时的 OPcache 状态,以验证它正在工作:

php
<?php $status = opcache_get_status(); echo "Cached scripts: " . $status['opcache_statistics']['num_cached_scripts'] . "\n"; echo "Cache hits: " . $status['opcache_statistics']['hits'] . "\n"; echo "Cache misses: " . $status['opcache_statistics']['misses'] . "\n"; echo "Free memory: " . $status['memory_usage']['free_memory'] . " bytes\n";

如果 free_memory 持续偏低,请调高 opcache.memory_consumption

参见