Every “fix memory exhausted” article tells you the same thing – bump WP_MEMORY_LIMIT to 256M and move on. That’s not a fix. That’s duct tape. The real question nobody answers is: what’s eating your memory in the first place?
I’ve profiled hundreds of WordPress sites. The pattern is always the same – someone raises the limit, things work for a while, then the error comes back. Because the actual problem is still there, just hiding behind a bigger number.
Let’s actually diagnose it.
Understanding WordPress Memory – The Two Limits You Need to Know
WordPress has two separate memory ceilings, and most people confuse them.
PHP’s memory_limit is the hard cap set in your server’s php.ini (or pool config). This is the absolute maximum any PHP process can use.
WordPress’s WP_MEMORY_LIMIT is a soft limit WordPress sets for itself. It’s defined in wp-config.php and can never exceed the PHP limit.
There’s also WP_MAX_MEMORY_LIMIT – which applies only to admin-side operations (wp-admin). WordPress sets this to 256M by default.
Here’s the thing most guides skip: if your PHP memory_limit is 128M and you set WP_MEMORY_LIMIT to 512M, you still only get 128M. WordPress can’t override the server. It can only request up to what PHP allows.
To check your actual limits, drop this in a mu-plugin:
<?php
// File: wp-content/mu-plugins/memory-check.php
add_action('admin_notices', function() {
if (!current_user_can('manage_options')) return;
$php_limit = ini_get('memory_limit');
$wp_limit = defined('WP_MEMORY_LIMIT') ? WP_MEMORY_LIMIT : '40M';
$wp_max_limit = defined('WP_MAX_MEMORY_LIMIT') ? WP_MAX_MEMORY_LIMIT : '256M';
$current_use = round(memory_get_usage() / 1024 / 1024, 2);
$peak_use = round(memory_get_peak_usage() / 1024 / 1024, 2);
echo '<div class="notice notice-info">';
echo "<p><strong>Memory:</strong> PHP limit: {$php_limit} | ";
echo "WP limit: {$wp_limit} | WP admin limit: {$wp_max_limit} | ";
echo "Current: {$current_use}MB | Peak: {$peak_use}MB</p>";
echo '</div>';
});
That gives you the full picture in one glance. If your peak usage is consistently close to the limit – say above 80% – you have a problem that needs investigating, not just a bigger number.
Where Memory Actually Goes in WordPress
PHP memory in WordPress gets consumed by four main things:
1. Plugin code loaded into memory. Every active plugin loads its PHP files on every single request. A plugin with 50 PHP files and a bunch of class autoloading? All of that sits in memory.
2. Database query results. When a plugin runs get_posts() without a limit, or loads the entire options table, all that data lives in PHP memory until the request ends. I wrote about this in the context of autoloaded options – a bloated autoload table eats memory on every single page load.
3. Image processing. When WordPress generates thumbnails or processes an uploaded image, it loads the full image into memory. A 5MB JPEG can easily consume 30-40MB of PHP memory when being manipulated by GD or Imagick.
4. Object caching and globals. WordPress stores a lot of intermediate data in memory – post objects, user objects, cached queries. If you’re running WooCommerce with thousands of products and a poorly written product query, that’s all sitting in RAM.
Step 1: Measure Current Memory Usage Per Page
Before you start disabling things randomly, measure. Add this to your theme’s functions.php or better yet, as a mu-plugin:
<?php
// File: wp-content/mu-plugins/memory-profiler.php
add_action('shutdown', function() {
if (!current_user_can('manage_options')) return;
if (defined('DOING_AJAX') && DOING_AJAX) return;
if (defined('REST_REQUEST') && REST_REQUEST) return;
$peak = memory_get_peak_usage(true);
$peak_mb = round($peak / 1024 / 1024, 2);
// Log to debug.log
error_log(sprintf(
'[Memory] %s - Peak: %sMB - URI: %s',
date('H:i:s'),
$peak_mb,
$_SERVER['REQUEST_URI'] ?? 'unknown'
));
});
Make sure WP_DEBUG and WP_DEBUG_LOG are enabled in wp-config.php. Then browse your site – hit the homepage, a few posts, the admin dashboard, the plugins page. Check wp-content/debug.log afterwards.
What you’re looking for: which pages use the most memory? If your homepage uses 45MB but the admin plugins page uses 120MB, that tells you something. If a specific post type archive spikes to 90MB, that’s worth investigating.
For a deeper look at your wp-config.php debug constants and what each one does, check the wp-config.php performance tuning guide.
Step 2: Find the Memory-Hungry Plugins
This is where it gets interesting. Most guides tell you to deactivate all plugins and reactivate one by one. That works, but it’s painfully slow and doesn’t give you numbers.
Here’s a mu-plugin that measures memory before and after each plugin loads:
<?php
// File: wp-content/mu-plugins/plugin-memory-audit.php
// WARNING: Dev/staging only. Remove after diagnosis.
add_action('pre_current_active_plugins', function() {
if (!current_user_can('manage_options')) return;
$active_plugins = get_option('active_plugins', []);
$measurements = [];
foreach ($active_plugins as $plugin) {
$file = WP_PLUGIN_DIR . '/' . $plugin;
if (!file_exists($file)) continue;
$before = memory_get_usage();
// Note: plugins are already loaded at this point
// This reads the file size as a proxy for code weight
$size = filesize($file);
$measurements[$plugin] = [
'main_file_kb' => round($size / 1024, 1),
];
}
// Sort by file size descending
uasort($measurements, fn($a, $b) => $b['main_file_kb'] <=> $a['main_file_kb']);
echo '<div class="notice notice-info"><pre>';
echo "Plugin Main File Sizes (larger = potentially more memory):\n\n";
foreach ($measurements as $plugin => $data) {
printf("%-50s %6.1f KB\n", basename(dirname($plugin)), $data['main_file_kb']);
}
echo '</pre></div>';
});
But the real way to measure per-plugin memory impact is with the binary deactivation method. It’s faster than one-by-one:
- Note your baseline peak memory (from Step 1)
- Deactivate half your plugins
- Measure again. Did memory drop significantly?
- If yes, the culprit is in the deactivated half. Reactivate them, deactivate the other half.
- Keep halving until you find the offender
With 20 plugins, this takes 4-5 rounds instead of 20 individual tests.
If you want to skip the manual work, Query Monitor shows memory usage per component. Install it, load a page, and check the “Environment” panel for current and peak memory. The “Queries by Component” panel shows you which plugins are running the most database queries – which correlates heavily with memory use.
Step 3: Investigate the Usual Suspects
In my experience diagnosing WordPress performance issues, these are the categories that eat the most memory:
Page builders (40-80MB). Elementor, Divi, WPBakery – they load massive amounts of PHP code and rendering logic on every request, even on pages that don’t use the builder. If you’re using Elementor on 3 pages but it’s loading its full stack on all 200 pages, that’s a problem.
WooCommerce (30-60MB). This is basically an application within an application. It’s legitimate memory usage, but it adds up fast when combined with WooCommerce extensions. If you have WooCommerce + 15 WooCommerce add-ons, don’t be surprised when you need 256MB+.
Multilingual plugins (20-40MB). WPML in particular loads a lot of translation data into memory. If you’re running a multilingual WooCommerce store, you’re stacking two heavy plugins together.
SEO plugins with real-time analysis (15-30MB). Yoast and Rank Math load content analysis libraries in the editor. This is mostly an admin-side concern.
Backup plugins during execution. When a backup runs, it can temporarily spike memory way beyond normal levels. If your memory errors happen at scheduled times, check your backup schedule.
For help diagnosing specific plugin conflicts, I wrote a separate guide on that.
Step 4: Check Your Database for Memory Bloat
A source of memory usage people rarely check: the database queries themselves.
Enable SAVEQUERIES temporarily in wp-config.php:
define('SAVEQUERIES', true);
Then add this to see what’s happening:
<?php
// Add to your memory-profiler mu-plugin
add_action('shutdown', function() {
if (!current_user_can('manage_options')) return;
if (!defined('SAVEQUERIES') || !SAVEQUERIES) return;
global $wpdb;
$total_queries = count($wpdb->queries);
$slow_queries = 0;
$total_time = 0;
foreach ($wpdb->queries as $q) {
$total_time += $q[1];
if ($q[1] > 0.05) $slow_queries++;
}
error_log(sprintf(
'[Queries] %s - Total: %d - Slow (>50ms): %d - Time: %.3fs - URI: %s',
date('H:i:s'),
$total_queries,
$slow_queries,
$total_time,
$_SERVER['REQUEST_URI'] ?? 'unknown'
));
});
Important: SAVEQUERIES itself uses extra memory because it stores every query in an array. Only enable it during diagnosis, then turn it off. I’ve seen sites where enabling SAVEQUERIES was enough to push them over the memory limit – which tells you something about how many queries they’re running.
If you’re seeing hundreds of queries per page load, that’s a memory problem disguised as a query problem. Every result set sits in PHP memory. A query returning 10,000 rows of post meta data? That’s all in RAM.
For a deeper dive into slow queries, check the slow queries diagnostic guide.
Step 5: The Autoload Trap
This one deserves its own callout because it’s so common. Every page load in WordPress runs one query to load all autoloaded options from wp_options. All of them. Into memory.
Check how much data that is:
SELECT SUM(LENGTH(option_value)) as autoload_size
FROM wp_options
WHERE autoload = 'yes';
If that number is over 1MB, you have a problem. I’ve seen sites with 5-10MB of autoloaded data – that’s 5-10MB of memory consumed before WordPress even starts rendering a page.
Common culprits: abandoned plugin settings that left autoload = 'yes' entries behind, transients stored with autoload on, and serialized blobs from plugins that cache data in options instead of using the object cache.
I wrote an entire post about the autoload problem and how to fix it. If your autoload size is over 1MB, start there.
Step 6: Image Processing Spikes
If your memory errors happen specifically during uploads, the problem is almost certainly image processing.
When you upload a 4000x3000px image, WordPress generates multiple thumbnail sizes. The GD library (default) needs to hold the entire uncompressed image in memory. The formula is roughly:
width x height x channels x bytes_per_channel
For a 4000×3000 RGBA image: 4000 * 3000 * 4 * 1 = ~46MB. Just for one image in memory. Now multiply that by each thumbnail being generated.
Solutions:
- Switch to Imagick if available – it streams from disk instead of loading everything into memory
- Limit maximum upload dimensions with
big_image_size_thresholdfilter (WordPress 5.3+) - Reduce registered image sizes – every size means another memory allocation during upload
// Limit uploaded images to 2560px max (WordPress default since 5.3)
// Lower it if you're hitting memory limits during uploads
add_filter('big_image_size_threshold', function() {
return 1920; // Max dimension in pixels
});
When to Actually Increase the Limit
Sometimes you genuinely need more memory. Here’s when increasing makes sense:
- You’re running WooCommerce with extensions – 256M is reasonable
- You have a legitimate need for heavy plugins (LMS, membership, multilingual + ecommerce)
- You’ve already optimized and your peak usage is still near the limit
And here’s how to set it properly:
// In wp-config.php - ABOVE the "That's all, stop editing!" line
define('WP_MEMORY_LIMIT', '256M'); // Frontend
define('WP_MAX_MEMORY_LIMIT', '512M'); // Admin/backend
But also check your PHP config. You can’t just set the WordPress constant – the PHP limit needs to match or exceed it. Ask your host, or check via phpinfo() or Site Health > Info > Server.
If your admin dashboard is slow, high memory usage is often part of the puzzle. Memory pressure causes PHP to spend more time on memory management, which slows everything down.
A Practical Diagnostic Workflow
Here’s the process I follow when someone reports memory errors:
- Check the limits. What’s PHP’s
memory_limit? What’sWP_MEMORY_LIMIT? Are they aligned? - Measure baseline. Drop in the memory profiler mu-plugin. Browse the site. Note peak usage on key pages.
- Check autoload size. Run the SQL query. If it’s over 1MB, clean it up first – that’s often the quickest win.
- Binary plugin test. Deactivate half, measure, narrow down. Find the top 2-3 memory consumers.
- Decide. Can you replace the heavy plugin? Optimize its settings? Or do you genuinely need to increase the limit?
- Monitor. Leave the memory logging mu-plugin active for a week. Watch for spikes that correlate with cron jobs, backups, or specific user actions.
The whole process takes about an hour for most sites. And it gives you actual data instead of blindly throwing memory at the problem.
Tools That Help
A few tools I use regularly for memory diagnosis:
- Query Monitor – Shows memory usage, query count per component, and hooks. Essential for any WordPress developer.
- WP Multitool – The System Info module shows your memory limits at a glance, Config Manager lets you toggle debug constants without editing files, and the Autoloader Optimizer helps clean up bloated autoload data. Full disclosure – I built this one.
- Code Profiler – If you need deep PHP-level profiling, this generates detailed charts showing which scripts, classes, and functions use the most resources.
- PHP’s built-in functions –
memory_get_usage()andmemory_get_peak_usage()are your friends. They’re simple, accurate, and don’t require any plugin.
The Takeaway
The memory exhausted error is a symptom, not a diagnosis. Increasing the limit without understanding what’s consuming the memory is like turning up the music to ignore a rattling engine.
Measure first. Find the actual consumer. Then decide whether to optimize it, replace it, or genuinely allocate more memory. In most cases I’ve seen, a combination of cleaning up autoloaded data and replacing one heavy plugin solves the problem without touching the memory limit at all.
If you’re seeing memory issues alongside database bloat or general slowness, those problems feed into each other. Fix them together.
Join developers and agency owners who get backend optimization strategies, tool releases, and deep-dive guides.
No spam. Unsubscribe anytime. We respect your privacy.
