Skip to content

[12.x] Fix memory leak in Arr::dot()#58458

Merged
taylorotwell merged 2 commits intolaravel:12.xfrom
benjamin-commentor:bug/memory-leak-arr-dot
Jan 22, 2026
Merged

[12.x] Fix memory leak in Arr::dot()#58458
taylorotwell merged 2 commits intolaravel:12.xfrom
benjamin-commentor:bug/memory-leak-arr-dot

Conversation

@benjamin-commentor
Copy link
Contributor

Description

In #55495 speed performance has been improved on the Arr::dot function, however it also introduced a memory leak within in the function. The $flatten variable, which referenced in its own closure is not garbage collected immediately after the function is finished. This results in reserved memory that persist after each call to the Arr::dot function. It is a small amount, but if called enough times the process can eventually run out of memory. An example of this happening is when a large request needs to be validated with a large amount of rules as Arr::dot is called many times during that validation.

The same problem has been raised in #57660, where the context was when running Octane, however this problem is not limited to Octane, but can provoke out of memory error on a single request as well. That same solution has been proposed in a comment in #57660 however it has not been reviewed.

This PR remove the reference to the $flatten closure after it has been used, so it can be garbage collected immediately.

Expected results

Memory used before and after the Arr::dot function should remain the same.

Actual results

Memory is increased for each call to Arr::dot

Steps to reproduce

Route::get('test/{itemCountMax}/{functionCountMax}', function (int $itemCountMax, int $functionCountMax) {
    $items = [];

    for ($itemCount = 0; $itemCount < $itemCountMax; $itemCount++) {
        $items['items'][] = ['foo' => 'bar'];
    }

    $beginUsage = memory_get_usage();
    $beginTime = microtime(true);
    gc_collect_cycles();
    for ($functionCount = 0; $functionCount < $functionCountMax; $functionCount++) {
        Arr::dot($items);
    }
    $endUsage = (memory_get_usage() - $beginUsage) / 1024 / 1024;
    $endTime = microtime(true) - $beginTime;
    dump("MB difference: $endUsage and " . gc_collect_cycles() . " GC cycles in $endTime seconds");
});

Benchmark

Using the above step to reproduce the problem with different size of array and number of function calls in the three implementations.

Array size (items) Function calls Implementation Memory usage (MB) Garbage Collection Time (s)
100 100 Pre #55495 0.02 0 0.009
100 100 Current 1.27 200 0.009
100 100 PR 0.02 0 0.008
1000 100 Pre #55495 0.02 0 0.17
1000 100 Current 7.83 200 0.08
1000 100 PR 0.02 0 0.07
5000 100 Pre #55495 0.02 0 3.2
5000 100 Current 50.43 200 0.4
5000 100 PR 0.02 0 0.3
10000 100 Pre #55495 0.02 0 13.1
10000 100 Current 100.75 200 0.8
10000 100 PR 0.02 0 0.6
1000 1000 Pre #55495 0.02 0 2.0
1000 1000 Current 78.11 2000 0.8
1000 1000 PR 0.02 0 0.7
1000 2500 Pre #55495 0.02 0 5.3
1000 2500 Current 195.25 5000 2.3
1000 2500 PR 0.02 0 1.6

Changing the two parameters: array size and function calls only have an effect on memory when used together with the current implementation, while the others are unaffected. All of them are of course affected in their runtime however it looks like the PR also increase speed by a small amount. The most significant change is the memory usage. Keep in mind the array used is incredibly simple and more elaborate arrays will consume more memory.

Conclusion

The Arr::dot function causes a memory leak after #55495 has been implemented in exchange for much faster execution. This PR eliminate the memory leak while still maintaining the same speed performance previously implemented.

@taylorotwell taylorotwell merged commit ae924df into laravel:12.x Jan 22, 2026
68 of 70 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

Comments