From e4d4f1cedd9690622f8e33931965e568fc72312a Mon Sep 17 00:00:00 2001 From: Dennis Snell Date: Tue, 17 Jun 2025 17:09:03 -0700 Subject: [PATCH] do_blocks(): Free up transient memory leak (#8999) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There has been a memory inefficiency inside of `do_blocks()` where it parses the given content then iterates through each top-level block to render it. Unfortunately each top-level block can considerably add to the overall memory use involved, and once moving on to the next top-level block, all of the allocated memory will be retained until the end of the call to `do_blocks()`. In this change, each parsed block sub-tree is freed via reset to `null` after it has been rendered. All top-level blocks are rendered independently of each other and so this operation is safe — there are no data dependencies between them. For a test post of length 2.3 MB containing 138 top-level blocks, this brought the minimum memory requirement for the PHP process down from around 450 MB to around 316 MB. This was “around” since the memory requirements are non-deterministic and some runs will succeed while other runs will crash for out-of-memory given the same `memory_limit`. Impact of this change will be most profound when there exist one or more top-level blocks which allocate a significant amount of memory which are not the last top-level-block in a post. This means that this change might even impact small and typical posts, if the right blocks are near the start of the post. Fixes #63588 --- src/wp-includes/blocks.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/wp-includes/blocks.php b/src/wp-includes/blocks.php index 838cd84a19921..c9b4145bff46a 100644 --- a/src/wp-includes/blocks.php +++ b/src/wp-includes/blocks.php @@ -2403,11 +2403,13 @@ function parse_blocks( $content ) { * @return string Updated post content. */ function do_blocks( $content ) { - $blocks = parse_blocks( $content ); - $output = ''; + $blocks = parse_blocks( $content ); + $top_level_block_count = count( $blocks ); + $output = ''; - foreach ( $blocks as $block ) { - $output .= render_block( $block ); + for ( $i = 0; $i < $top_level_block_count; $i++ ) { + $output .= render_block( $blocks[ $i ] ); + $blocks[ $i ] = null; } // If there are blocks in this content, we shouldn't run wpautop() on it later.