TL;DR; if you have this problem. Disable the JIT PHP feature, specifically by adding these lines to your php.ini:
pcre.jit=0
opcache.jit=0
“My wp-admin is down agian.”
As a hosting engineer, that’s an email I never want to get. The client has a complex WooCommerce site doing over $200K a month in sales, he cannot afford the site to be unreliable. My business reputation depends on being able to provide very high uptime. The clock is ticking.
Looking into the error logs was even more confusing:
PHP Fatal error: Allowed memory size of 536870912 bytes exhausted (tried to allocate 4295229440 bytes) in /wp-includes/theme.php on line 325
PHP Fatal error: Allowed memory size of 536870912 bytes exhausted (tried to allocate 4295229440 bytes) in /wp-includes/theme.php on line 325
PHP Fatal error: Allowed memory size of 536870912 bytes exhausted (tried to allocate 4295229440 bytes) in /wp-includes/theme.php on line 325
PHP had used over 4GB of memory on running the following function:
return apply_filters( 'template', get_option( 'template' ) );
The line is innocent, It loads the theme name, a short string such as “astra” and applies some filters. How could that be using 4GB of memory?
As I was investigating the error, logging all of the filters attached to ‘template’ and ‘get_option’, the error resolved itself and was impossible to reproduce. For about a week. Then it happened again. And again. Each time the site would go down for 1-10 minutes before fixing itself. Then another client complained about the same error. Then another.
I won’t bore you with the details of everything I tried, but it included:
- Searching the internet and ChatGPT for answers.
- Searching for filters on ‘template’ and get_option(‘template’)
- Looking for corrupted data.
- Looking at WordPress core code until my eyes bled.
- Eventually, replacing the line with the following
// WHY???
// return apply_filters( ‘template’, get_option( ‘template’ ) );
return ‘astra’;
Why exactly 4295229440 bytes?
The maximum amount that a 32 bit unsigned integer can hold is 4,294,967,295. The amount of memory that PHP tried to allocate for itself is just slightly over this amount 🤔
“PHP” no longer runs PHP Code
PHP 5.5 introduced the op-cache. Pre-compiling *.php files into executable op-code. In my experience this roughly doubles the speed of PHP. Amazing. However, when op-cache is enabled, the op-code that is run may not match exactly the *.php file stored on the disk. It allows space for strange things to happen.
PHP 8.0 introduced another optimisation called JIT (just in time) compiling, this allows the interpreter more freedom to “interpret” the PHP code into more optimised machine code based on how the file is actually being run. The optimised machine code is then saved to the op-cache and executed next time. Very clever, but it takes us further away from the PHP script that we can reason about.
In my case, it seems that the JIT compiler was occasionally mis-optimising the theme loading code in a way that caused an integer overflow when calculating the amount of memory to allocate. This code was then saved to the op-cache, so for the next few minutes, every request to that worker would cause an out of memory error. In a few minutes, the JIT would run again and fix its mistake.
Fixed for good.
For now, I’ve disabled JIT compiling for PHP 8.3.x. Finding PHP language bugs is rare, but sometimes you have to do a very deep dive and even … sacrifice a little performance … in order to get the peace that comes when everything is working reliably.
If you have any issues with your site, I’m providing free one hour hosting support calls to diagnose and fix whatever issue you’re experiencing (valid if you run a serious business/ecommerce site and are based in Australia)