PHP OPcache JIT 调试信息如何定位未编译的函数
定位 PHP OPcache JIT 没有编译某个函数,最有效的办法不是先看耗时,而是建立两类证据:先确认当前 SAPI 中 JIT 确实处于 enabled/on 状态,再用 opcache.jit_debug 的 ASM 或 tracing 位观察“哪些函数或 trace 已产生编译输出”。目标函数没有出现时,再结合触发模式、热度阈值、trace 终止与黑名单信息继续排查。
官方配置说明:https://www.php.net/manual/en/opcache.configuration.php
这里有一个容易踩的版本差异:PHP 8.4 起 opcache.jit 的默认值是 disable。即使 opcache.jit_buffer_size 有非零默认值,也不能据此认定 JIT 已经运行。排查必须从实际加载的配置开始。
先排除 JIT 根本没有运行
问题现场通常是“这个计算函数跑了很多次,性能却和关闭 JIT 差不多”。这个现象只能提出怀疑,不能证明函数未编译。我们先确认 CLI 用的是哪份配置、OPcache 是否加载,以及 JIT 的全局状态。
# 确认 PHP 版本、配置文件和 OPcache 扩展实际来源 php -v php --ini php --ri "Zend OPcache" # 只读取全局 JIT 状态,不展开脚本缓存清单 php -d opcache.enable_cli=1 -r ' // 输出 enabled、on、kind、buffer_size 与 buffer_free var_export((opcache_get_status(false) ?: [])["jit"] ?? null); '
读结果时先看四个条件:opcache.enable_cli=1、jit.enabled=true、jit.on=true、buffer_size>0。其中任何一个不满足,都应该先修正加载配置,而不是分析某个函数为什么没有出现在调试信息里。
opcache_get_status(false) 适合证明 JIT 的全局状态,但它不是逐函数编译清单。buffer_free 很低可以提示容量压力,却不能直接证明具体函数未编译。

用最小脚本制造可观察目标
复杂框架会把自动加载、代理类、异常处理和业务分支混在一起,函数名也可能被闭包或命名空间包装。先准备一个函数名稳定、调用次数可控的最小脚本,能够把“没有运行到”与“运行了但没有编译”分开。
这段脚本的目的不是跑分,而是让函数名、调用路径和触发次数都可控。排查真实项目时,也要把同样的三个要素记下来:准确函数名、真实 SAPI、能稳定到达该函数的输入。
先用 ASM 输出建立已编译名单
官方 JIT RFC 与当前 zend_jit.h 都把 bit 0 定义为生成汇编输出,也就是 opcache.jit_debug=1。为了先排除“热度还没达到”这个变量,可以在隔离 CLI 中临时使用 function 模式。该模式对应 CRTO 1205,触发位 T=0,会在脚本加载阶段尝试编译函数。
# 在隔离 CLI 中启用 function JIT,并把高噪声调试信息单独保存 php -d opcache.enable_cli=1 \ -d opcache.jit_buffer_size=64M \ -d opcache.jit=function \ -d opcache.jit_debug=1 \ jit-target.php 2>jit-asm.log # 只检索目标函数名,建立“出现过”的正向证据 grep -E 'sumEven|formatLabel' jit-asm.log
若日志中能找到某个函数对应的 JIT 汇编段,它就是明确的已编译证据。若一个函数没有出现,结论只能是“当前复现条件下尚未看到编译输出”,还不能立刻写成“JIT 不支持它”。下一步要检查它是否真的被加载、名称是否被命名空间改变,以及当前 PHP 分支使用的 debug 位是否一致。
不要把 opcache.opt_debug_level 和 opcache.jit_debug 混在一起。前者打印优化前后的 opcode,适合观察 OPcache 优化阶段;后者控制 JIT 的汇编、SSA、trace 等调试输出。本文要找 JIT 编译证据,主入口是后者。
再看 tracing JIT 的编译与终止信息
生产常用的 tracing 模式对应 CRTO 1254。它按运行中的热路径形成 trace,不保证“整个函数”以一个完整单元出现。目标函数没出现在 ASM 清单中,可能只是路径未变热,也可能 trace 在形成过程中中止。
按当前 PHP 源码,TRACE_COMPILED 是 1,TRACE_ABORT 是 1,TRACE_BLACKLIST 是 1。组合值是 0x34000。这些内部调试位可能随版本变化,实际使用前应查看与安装版本匹配的 zend_jit.h,不要永远照搬 master 分支。
# 观察已编译 trace、终止与黑名单事件;位值应先和当前 PHP 源码分支核对 php -d opcache.enable_cli=1 \ -d opcache.jit_buffer_size=64M \ -d opcache.jit=tracing \ -d opcache.jit_debug=0x34000 \ jit-target.php 2>jit-trace.log # 分别搜索函数名和 trace 相关事件,避免被大量输出淹没 grep -E 'sumEven|formatLabel|TRACE' jit-trace.log
解释结果时可以这样推进:
- 能看到目标路径的 compiled 信息:说明该热路径已经形成 JIT 代码。
- 只看到 abort:先看终止位置和代码形态,不要马上提高热度阈值。
- 反复出现 blacklist:说明运行时已停止继续尝试某类 root/side trace,需要结合终止原因和对应限制排查。
- 完全没有目标相关信息:先确认请求或脚本确实执行到目标路径,再检查 hot 计数与 profiling 触发条件。

把缺失证据映射到具体原因
| 观察到的证据 | 优先判断 | 下一步 |
|---|---|---|
| 所有 JIT 调试输出都为空 | 扩展、SAPI、JIT 开关或输出去向有误 | 复查 php --ini、OPcache 状态与 stderr |
| 其他函数有 ASM,目标函数没有 | 目标未加载、名称不同或代码形态未被编译 | 先用 function 模式和最小脚本复现 |
| function 模式出现,tracing 模式不出现 | 真实负载未达到热度或未走到该路径 | 核对调用次数、输入分支与 hot 参数 |
| 出现 TRACE_ABORT | trace 形成过程中被终止 | 按终止位置缩小代码形态和控制流 |
| 出现 TRACE_BLACKLIST | root/side trace 多次失败后不再尝试 | 结合 blacklist 阈值与先前 abort 信息 |
| buffer_free 接近耗尽 | JIT 缓冲区可能存在容量压力 | 在测试环境扩大缓冲并对照,但不把它当逐函数证据 |
opcache.jit_prof_threshold 只影响“首个请求采样后编译热点函数”的触发模式;opcache.jit_hot_func、opcache.jit_hot_loop 等则影响动态热度判断。排查时先认清 CRTO 的 T 位,再调整对应参数,否则会出现“改了阈值但行为不变”的假线索。
opcache.jit_bisect_limit 也不适合拿来列出未编译函数。它的用途是把 JIT 编译限制在前 N 个函数,帮助二分错误编译来源,而且官方说明它只在触发位为 0 或 1 时有效。正常定位缺失函数,优先使用正向编译输出和 trace 事件。
恢复配置并完成复查
调试位会产生大量底层信息,排查结束后应恢复 opcache.jit_debug=0,删除临时日志,并用与问题相同的 SAPI 和负载重新确认。不要把高噪声 ASM 或 IR 输出长期放在生产 worker 上。
最终复查至少回答四个问题:
- 目标函数在相同 PHP 版本、SAPI 和配置下是否稳定执行?
- JIT 全局状态是否同时满足 enabled、on 和非零 buffer?
- function 模式能否建立该函数的正向编译证据?
- tracing 模式缺席时,是否有热度不足、abort 或 blacklist 的对应证据?
只要按“全局开关—可重复目标—已编译名单—trace 事件—容量与阈值”的顺序推进,就能把模糊的“JIT 好像没生效”缩小成一个可验证原因。最重要的原则是:调试输出中的出现可以证明已编译,缺席却必须结合触发模式和运行路径解释。
常见问题
为什么 opcache_get_status 显示 JIT on,函数仍没有调试输出?
on 只说明 JIT 全局可用,不说明每个函数都满足触发条件。继续核对 SAPI、函数是否执行、CRTO 触发位、热度阈值和 debug 位。
opcache.jit_debug=1 能直接列出所有未编译函数吗?
不能。它输出已经生成的汇编,是正向证据。未出现的函数还需要与加载路径、调用次数、模式和 trace 事件交叉判断。
调试时应选 function 还是 tracing?
想先建立函数级正向证据,可在隔离环境用 function 模式;想解释真实 tracing 行为,则切回 tracing 并观察 compiled、abort 与 blacklist。两者回答的问题不同。
为什么不建议直接在生产打开全部 debug 位?
ASM、SSA、IR 和 trace 输出量可能很大,也会泄露内部函数与路径信息。应在可控复现环境逐个启用所需位,并在结束后恢复为 0。
EnableFullDuplex 为什么只对部分协议有效
- 上一篇
- EnableFullDuplex 为什么只对部分协议有效
- 下一篇
- Go HTTP Server 如何只启用 HTTP/1 与 HTTP/2
-
- 文章 · php教程 | 5小时前 |
- PHP match 表达式怎样覆盖枚举分支并保持穷尽
- 377浏览 收藏
-
- 文章 · php教程 | 8小时前 | php教程 · PHP生成器 yield from Generator send getReturn
- PHP 生成器如何双向传值并接收最终返回值
- 208浏览 收藏
-
- 文章 · php教程 | 10小时前 |
- PHP readonly 类继承时有哪些属性限制
- 223浏览 收藏
-
- 文章 · php教程 | 12小时前 |
- PHP ReflectionReference 如何判断数组元素是否共享引用
- 376浏览 收藏
-
- 文章 · php教程 | 14小时前 | php教程 · PHP 8.4 · php ReflectionClass newLazyGhost newLazyProxy lazy object 重量级服务
- PHP lazy object 如何延迟创建重量级服务
- 202浏览 收藏
-
- 文章 · php教程 | 16小时前 | 面向对象 · PHP · PHP 8.4 · PHP非对称属性可见性 private(set) protected(set) PHP 8.4属性 PHP对象封装
- PHP 非对称属性可见性如何限制对象外部写入
- 216浏览 收藏
-
- 文章 · php教程 | 18小时前 | 内存管理 · php教程 · 弱引用 PHP 8 SplObjectStorage PHP WeakMap 对象元数据
- PHP WeakMap 为什么适合保存对象附加元数据
- 227浏览 收藏
-
- 文章 · php教程 | 22小时前 | PHP · 异步编程 · php教程 · 异步回调 事件循环 PHP Fiber Fiber suspend Fiber resume
- PHP Fiber 如何让同步接口适配事件循环
- 272浏览 收藏
-
- 文章 · php教程 | 1天前 |
- PHP readonly 对象适合配置值还是领域实体
- 178浏览 收藏
-
- 文章 · php教程 | 1天前 | PHP · php-fpm · PHP OPcache opcache_reset validate_timestamps revalidate_freq opcache_invalidate
- OPcache 更新代码后仍命中旧脚本,该检查哪些配置
- 382浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 395次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 474次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 479次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 423次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 250次使用
-
- golang实现PHP数组特性的方法
- 2023-02-16 371浏览
-
- PHP与Go语言之间的通信详解
- 2023-01-07 347浏览
-
- php和go语言的区别有哪些
- 2023-03-04 112浏览
-
- HTTP 的 response 中的响应体和头部是分开发送的吗?
- 2023-01-28 387浏览
-
- 如何不停机升级机器的配置
- 2023-02-16 142浏览

