Skip to content

Keep a single memoization layer for located reflections - #5974

Merged
ondrejmirtes merged 2 commits into
2.2.xfrom
single-reflection-memoization-layer
Jul 2, 2026
Merged

Keep a single memoization layer for located reflections#5974
ondrejmirtes merged 2 commits into
2.2.xfrom
single-reflection-memoization-layer

Conversation

@ondrejmirtes

Copy link
Copy Markdown
Member

Located reflections were cached twice — by MemoizingReflector (keyed by name) and again by the MemoizingSourceLocator wrapper (keyed by identifier), pinning every reflection object graph in memory twice per worker process.

The locator-level cache could not simply be dropped: created reflections capture the reflector passed to SourceLocator::locateIdentifier() and resolve parent classes, interfaces and prototypes through it, bypassing MemoizingReflector entirely — the memoizing locator was the only cache absorbing those internal lookups (removing it naively OOMs workers because every parent/interface lookup re-locates from scratch).

Instead of wrapping DefaultReflector, MemoizingReflector now locates identifiers itself and passes $this to the source locator, so created reflections capture the memoizing reflector and route their internal lookups through the same name-keyed cache. This makes the MemoizingSourceLocator wrappers redundant for real:

  • removed from BetterReflectionSourceLocatorFactory, StubSourceLocatorFactory and TestCaseSourceLocatorFactory, together with the cache.memoizingSourceLocatorEntriesCountMax parameter (measurements showed capping that layer was counterproductive anyway),
  • the originalBetterReflectionReflector service is gone,
  • the stub validator container relied on the removed wrapper as its only memoization, so it now overrides betterReflectionSourceLocator! with the stub source locator instead of swapping reflectors — stubReflector is gone too.

Results (self-analysis, warm caches, 8 workers; "Used memory" = sum of per-worker peaks):

Used memory elapsed
2.2.x 2.49–2.52 GB ~26 s
with this PR 2.11–2.13 GB ~26 s

Full test suite passes. No BetterReflection change needed — works with the released 6.70.0.3.

🤖 Generated with Claude Code

https://claude.ai/code/session_017DjgMHgfvwhD4ogdTaRtLp

ondrejmirtes and others added 2 commits July 2, 2026 17:58
Located reflections were cached twice - by MemoizingReflector (keyed
by name) and again by MemoizingSourceLocator (keyed by identifier),
pinning every reflection object graph in memory twice per worker
process.

The locator-level cache could not simply be dropped: created
reflections capture the reflector passed to
SourceLocator::locateIdentifier() and resolve parent classes,
interfaces and prototypes through it, bypassing MemoizingReflector
entirely - the memoizing locator was the only cache absorbing those
internal lookups. MemoizingReflector now locates identifiers itself
instead of wrapping DefaultReflector, so created reflections capture
the memoizing reflector and route internal lookups through the same
name-keyed cache. The MemoizingSourceLocator wrappers are removed
along with the cache.memoizingSourceLocatorEntriesCountMax parameter
and the originalBetterReflectionReflector service.

The stub validator container relied on the removed locator wrapper as
its only memoization, so it now overrides betterReflectionSourceLocator
with the stub source locator instead of swapping reflectors.

In self-analysis this reduces total worker peak memory
from ~2.5 GB to ~2.1 GB with unchanged elapsed time.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017DjgMHgfvwhD4ogdTaRtLp
It was only needed so that the stub validator container could point
NodeScopeResolver to a different reflector. Since the stub validator
now overrides the source locator instead, the service is just an alias
of betterReflectionReflector, which is what a plain Reflector type-hint
autowires to anyway.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017DjgMHgfvwhD4ogdTaRtLp
@ondrejmirtes
ondrejmirtes merged commit 9898ba5 into 2.2.x Jul 2, 2026
359 of 361 checks passed
@ondrejmirtes
ondrejmirtes deleted the single-reflection-memoization-layer branch July 2, 2026 16:04
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.

1 participant