Zend OPcache 概念、安裝與設定:php.ini 的值與停用 JIT

測試環境 RHEL 系列AlmaLinux 9.8root 帳號2026-10-16 確認

Zend OPcache 是一個 PHP 擴充功能,會把 PHP 指令碼編譯成位元組碼並存放在共用記憶體中,讓同一個檔案再次執行時可以略過編譯。在使用 PHP 8.3 的 WordPress 伺服器上啟用 JIT 後,所有頁面都出現 500 錯誤,於是保留 OPcache 不動,只停用 JIT 來恢復。

從 PHP 5.5 起,OPcache 就預設包含在 PHP 中一併發布。

1️⃣ Zend OPcache 概念:位元組碼快取

每當有請求進來,PHP 指令碼都會經過三個階段:讀取檔案、編譯成位元組碼、執行。OPcache 運作時,從同一個檔案的第二次請求開始,前兩個階段會省掉,只剩下執行。

OPcache 用來存放位元組碼的共用記憶體,由 PHP-FPM 的所有工作程序共同使用。如果有多個網站連到同一個 PHP-FPM,這些網站的指令碼也會存放在同一塊共用記憶體中。

🧩 沒有 OPcache 與有 OPcache 時的請求處理
說明圖解
🧠 OPcache
📨 請求
🧰 PHP-FPM Worker
工作程序 1第一次請求
工作程序 2第二次請求
工作程序 3待命
每次請求都要三個階段第一次請求 · 三個階段只有執行→
⚙️ PHP 指令碼處理
讀取檔案省略
→
編譯成位元組碼省略
→
執行執行 PHP · 查詢資料庫
不使用共用記憶體↓ 儲存位元組碼↑ 使用共用記憶體中的位元組碼
🧠 共用記憶體
位元組碼(已編譯的 PHP 程式碼)
PHP-FPM 的所有工作程序共同使用 · 即使有多個網站也只有一塊共用記憶體
沒有 OPcache 時,PHP 指令碼每次收到請求都會經過三個階段:讀取檔案、編譯成位元組碼、執行。
沒有 OPcache 時,同一個檔案的第二次請求也會再經過三個階段:讀取檔案、編譯成位元組碼、執行。
OPcache 運作時,第一次請求會經過三個階段,然後把位元組碼存進共用記憶體。
從同一個檔案的第二次請求開始,前兩個階段會省掉,只剩下執行;共用記憶體由 PHP-FPM 的所有工作程序共同使用。

OPcache 儲存的是編譯後的 PHP 程式碼,所以每次開啟頁面時,PHP 的執行與資料庫查詢仍然會發生。把完成的 HTML 存起來、直接略過 PHP 執行,則是頁面快取的工作。

2️⃣ 確認 PHP OPcache 是否已安裝

OPcache 是否已安裝,可以從 [php -v] 輸出的最後一行確認。

php -v
PHP 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 Technologies

如果沒有最後一行,就用 dnf 安裝。套件名稱通常是 php-opcache;如果是用 Remi 套件庫一次安裝了多個 PHP 版本,名稱會加上版本,例如 php83-php-opcache。

dnf -y install php-opcache

Remi 套件庫的 php-opcache 套件,會透過 [/etc/php.d/10-opcache.ini] 裡的 [zend_extension=opcache] 這一行載入擴充功能。安裝後重新啟動 PHP-FPM,OPcache 就會套用到網頁請求上。

3️⃣ OPcache 設定:php.ini 的值

OPcache 的設定可以放在兩個地方:[/etc/php.ini] 的 [opcache] 區段,以及 [/etc/php.d/10-opcache.ini]。

php --ini
Configuration 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,

php.d 資料夾中的檔案會在 php.ini 之後讀取,所以同一個項目寫在兩個地方時,會套用 php.d 那邊的值。

下表是 PHP 8.3 的預設值,以及實際檢查的 WordPress 伺服器上套用的值。

項目PHP 8.3 預設值套用值說明
opcache.enable11使用 OPcache
opcache.enable_cli01命令列 PHP(wp-cli 等)也會使用
opcache.memory_consumption1281024存放位元組碼的共用記憶體(MB)
opcache.interned_strings_buffer816相同字串只存一次的記憶體(MB)
opcache.max_accelerated_files1000010000可快取的鍵數量上限(實際上限是大於或等於此值的質數,設為 10000 時為 16229)
opcache.max_wasted_percentage510記憶體不足且被浪費的空間超過此比例時,會清空快取並重新開始
opcache.validate_timestamps11檢查檔案是否有變更
opcache.revalidate_freq260檢查檔案變更的間隔(秒)
opcache.jittracingdisableJIT 的方式(從 PHP 8.4 起預設為 disable)
opcache.jit_buffer_size00JIT 記憶體,設為 0 時 JIT 不會運作(從 PHP 8.4 起預設為 64M)

放進 [/etc/php.ini] 的 [opcache] 區段的值如下。記憶體大小依伺服器記憶體決定,[opcache.max_accelerated_files] 則依 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=60

如果在 php.ini 裡再加一行 [zend_extension],擴充功能會被載入兩次,並輸出 [Cannot load Zend OPcache] 警告。

[opcache.revalidate_freq] 為 60 時,修改 PHP 檔案後,舊的程式碼最多還可能再執行 60 秒。

PHP 檔案經常變動的 WordPress 伺服器,要把 [opcache.validate_timestamps] 設為 1,讓系統檢查檔案是否變更。Symfony 文件建議正式環境伺服器使用的 0,適合只有在部署新版本時才會更動 PHP 檔案的伺服器。

即使把 [opcache.validate_timestamps] 設為 0,在管理畫面進行更新時,WordPress 仍會自行清除已變更檔案的快取。但如果用 wp-cli 更新,或透過 SSH 修改檔案,在重新啟動 PHP-FPM 之前,仍會執行舊的程式碼。

4️⃣ 停用 OPcache 的 JIT

PHP 8 的 JIT 是讓大量 CPU 運算的工作變快的功能,所以對像 WordPress 這樣處理網頁請求的應用程式,效果很小。PHP JIT RFC 中刊載的 WordPress 測量結果也是如此:啟用 JIT 時每秒處理 326 個請求,停用時為 315 個。

在使用 PHP 8.3.22 的 WordPress 伺服器上,以 [opcache.jit=tracing] 與 [opcache.jit_buffer_size=128M] 啟用了 JIT。三天後,請求湧入、PHP-FPM 的工作程序多次重新啟動的那一天,所有頁面都發生了 500 錯誤。

錯誤記錄中留下了 [Allowed memory size of 1073741824 bytes exhausted] 這則訊息。接在後面的 [tried to allocate 4295229440 bytes],是在 PHP 記憶體上限 1GB 的情況下,一次想配置 4GB 的紀錄。停用 JIT 後,相同的請求就能正常處理了。

要停用 JIT,在 [/etc/php.d/10-opcache.ini] 加入下面兩行。即使 [/etc/php.ini] 裡還留著 JIT 設定,也會套用這個檔案的值。

opcache.jit=disable
opcache.jit_buffer_size=0

PHP 8.3 的 [opcache.jit_buffer_size] 預設值是 0,所以沒有另外設定的話,JIT 不會運作。從 PHP 8.4 起,[opcache.jit] 的預設值也是 disable。

5️⃣ 套用設定並確認

修改後的設定要重新啟動 PHP-FPM 才會生效。

systemctl restart php-fpm

重新啟動後,OPcache 的共用記憶體會被清空,並依新設定重新配置。

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 => On

[php -i] 輸出的值是命令列 PHP 的設定。在 RHEL 系列中,PHP-FPM 也讀取相同的 [/etc/php.ini] 與 [/etc/php.d] 檔案,所以值是一樣的。

在命令列呼叫 [opcache_get_status()],快取的指令碼會顯示為 0 個。命令列 PHP 每次執行都會重新建立共用記憶體,所以 PHP-FPM 的使用量要把下面的檔案放進網頁文件資料夾,再用瀏覽器確認。

<?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']);

[cache_full] 為 true 時,表示共用記憶體或鍵已經滿了。確認完之後,請立刻刪除這個檔案。

在三個 WordPress 網站共用同一個 PHP-FPM 的伺服器上,1024MB 共用記憶體中用掉了 317MB。每個網站快取的指令碼有 3,366 到 4,222 個,總共 11,358 個。

OPcache 以實際路徑儲存指令碼,對於用相對路徑載入的檔案,還會多建立一個附上工作目錄的鍵,所以鍵的數量會比指令碼數量多。指令碼有 11,358 個時,鍵填滿了 16,229 個的上限,16MB 的字串緩衝區也沒有剩餘空間。

鍵滿了之後,新的指令碼不會被快取,每次請求都要編譯。在 5 分 21 秒內,快取未命中從 18,408 次增加到 36,027 次,但快取的指令碼數量仍維持 11,358 個。

執行多個 WordPress 或外掛很多的伺服器,要把 [opcache.max_accelerated_files] 設得寬裕一些,避免鍵的上限被填滿。設為 20000 時,實際的鍵上限是 32,531 個。如果字串緩衝區已滿,也要一併調高 [opcache.interned_strings_buffer]。

🔢 FAQ & 推薦內容

這項設定已在 PHP 7.2 移除,所以在 PHP 8 中即使還留在 php.ini,也不會影響運作。如果是沿用舊設定的伺服器,這一行可以刪掉。

OPcache 儲存編譯後的 PHP 程式碼,Redis 物件快取儲存資料庫查詢結果。由於儲存的對象不同,兩者可以一起使用。

以 PHP-FPM 執行 PHP 的伺服器,OPcache 是在 PHP-FPM 裡運作,所以只要重新啟動 PHP-FPM。以 Apache 模組(mod_php)執行 PHP 的伺服器,則要重新啟動 Apache。

ℹ️ 聯盟行銷說明
本站內容包含聯盟行銷連結。訪客透過這些連結購買商品或服務時,本站會從賣家獲得佣金。在這個過程中,購買者支付的金額(遇到活動折扣時金額會降低↓)並不會因此提高。刊登的價格、折扣與庫存資訊以撰寫當時為準,可能與實際情況不同,購買前請向賣家做最後確認。商品的挑選與評價依本站自訂的標準撰寫,是否支付佣金不會影響介紹順序或評價內容。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

Prove your humanity: 2   +   1   =