From deec6283f424a76f3fafc60a0b417207017e78d7 Mon Sep 17 00:00:00 2001 From: audrasjb Date: Tue, 8 Jul 2025 09:01:25 +0000 Subject: [PATCH] Editor: Free up transient memory leak in `do_blocks()`. 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. This commit also includes follow-up changes committed in [60400] and [60402]. Rewieved by audrasjb. Merges [60316], [60400] and [60402] to the 6.8 branch. Props dmsnell, joemcgill. Fixes #63588. Built from https://develop.svn.wordpress.org/branches/6.8@60435 git-svn-id: http://core.svn.wordpress.org/branches/6.8@59771 1a063a9b-81f0-0310-95a4-ce76da25c4cd --- wp-includes/blocks.php | 29 +++++++++++++++++++++++++---- wp-includes/version.php | 2 +- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/wp-includes/blocks.php b/wp-includes/blocks.php index 8472c7e092..56410779fe 100644 --- a/wp-includes/blocks.php +++ b/wp-includes/blocks.php @@ -2404,11 +2404,32 @@ 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 ); + /** + * Parsed blocks consist of a list of top-level blocks. Those top-level + * blocks may themselves contain nested inner blocks. However, every + * top-level block is rendered independently, meaning there are no data + * dependencies between them. + * + * Ideally, therefore, the parser would only need to parse one complete + * top-level block at a time, render it, and move on. Unfortunately, this + * is not possible with {@see \parse_blocks()} because it must parse the + * entire given document at once. + * + * While the current implementation prevents this optimization, it’s still + * possible to reduce the peak memory use when calls to `render_block()` + * on those top-level blocks are memory-heavy (which many of them are). + * By setting each parsed block to `NULL` after rendering it, any memory + * allocated during the render will be freed and reused for the next block. + * Before making this change, that memory was retained and would lead to + * out-of-memory crashes for certain posts that now run with this change. + */ + 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. diff --git a/wp-includes/version.php b/wp-includes/version.php index 3130973e27..5b7c77f1f2 100644 --- a/wp-includes/version.php +++ b/wp-includes/version.php @@ -16,7 +16,7 @@ * * @global string $wp_version */ -$wp_version = '6.8.2-alpha-60434'; +$wp_version = '6.8.2-alpha-60435'; /** * Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.