Static $__joinPoint keeps the first instance of every woven class alive
Los mantenedores suelen responder en 1 día
Ya se ha fusionado un pull request relacionado.
- #802 de @lisachenko — fusionado
Evaluación
- Dificultad
- 4/5
- Tiempo estimado
- 3-5 días
- Aptitud para principiantes
- 22/100
Línea de trabajo
Start with the generated static $__joinPoint initialiser and AbstractMethodInvocation::$closureToCall, then read DynamicTraitAliasMethodInvocation::resolveOriginalMethod(), which uses the closure only to find the method. Reproduce the leak with the WeakReference snippet from the issue. Done means the first instance is collectable after gc_collect_cycles() without changing invocation results, and composer test:performance shows no regression. A linked pull request (#802) already exists, so check it before starting.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
What happens
Each intercepted instance method creates its joinpoint once, in a function-scoped static:
static $__joinPoint = InterceptorInjector::forMethod(
self::class, 'sum', [...],
$this->sumOriginalAlias(...), // captures $this of the first call
);
The first-class callable captures the object of the first call. It is stored in AbstractMethodInvocation::$closureToCall (protected readonly Closure) and never released, so that object can't be garbage-collected until the end of the request:
$a = new Calc(); $a->sum(1, 1); $wa = WeakReference::create($a); unset($a); gc_collect_cycles();
$b = new Calc(); $b->sum(1, 1); $wb = WeakReference::create($b); unset($b); gc_collect_cycles();
var_dump($wa->get() !== null, $wb->get() !== null); // true, false: the first instance is still alive
The cost is one object per intercepted method, plus whatever that object references: entities, request objects, large graphs. Destructors of those objects run late. This isn't specific to PHP 8.6; it was found while checking whether PFA could replace this callable. PFA can't: 8.6 has no placeholder for $this, which the PFA RFC lists as future scope.
Cause
DynamicTraitAliasMethodInvocation only uses the closure to find the method: resolveOriginalMethod() reads the scope class and name, then calls ReflectionMethod::invokeArgs() on the current instance. The captured $this is never used.
Proposed fix (options)
- Pass the alias name instead of a callable, for example
InterceptorInjector::forMethod(self::class, 'sum', [...], 'sumOriginalAlias'), and resolvenew ReflectionMethod(self::class, 'sumOriginalAlias')lazily. For inherited methods without an alias, resolve the prototype from the parent class. This needs a migration path for theforMethod()signature and for existing caches. - Smaller fix: unset the closure after
resolveOriginalMethod(). This needs$closureToCallto stop beingreadonlyfor dynamic invocations, and it only frees the instance after the first call reaches the original body. An around advice that never proceeds keeps it alive.
Option 1 also makes the generated code a bit smaller. Check it with composer test:performance, since this touches the hot path.
Part of #789.
- Lenguaje dominante
- PHP
- Estrellas
- 1.7k
- Forks
- 166
- Merge medio
- 1 d 1 h
- PR fusionados (30 d)
- 43
Preparar el entorno
Este proyecto no incluye contenedor de desarrollo, Dockerfile ni guía de contribución, así que la configuración corre por tu cuenta: empieza por su README y consulta nuestra guía para la primera contribución para los pasos generales.
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de goaop/framework
-
Bug
Dificultad 4/5 1-2 días Aptitud para principiantes 56/100
Los mantenedores suelen responder en 1 día
-
Bug
Dificultad 4/5 3-5 días Aptitud para principiantes 45/100
Los mantenedores suelen responder en 1 día
-
Enhancement Performance
Dificultad 4/5 3-5 días Aptitud para principiantes 48/100
Los mantenedores suelen responder en 1 día
-
Enhancement Performance To Be Discussed
Dificultad 5/5 Más de una semana Aptitud para principiantes 28/100
goaop/framework#814 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
Enhancement PHP-8.6
Dificultad 3/5 1-2 días Aptitud para principiantes 35/100
Los mantenedores suelen responder en 1 día
Todos los issues de goaop/framework
Issues similares
-
sync-en
Dificultad 1/5 1-3 horas Aptitud para principiantes 82/100
Los mantenedores suelen responder en 1 día
-
feature-request-triage
Dificultad 2/5 1-3 horas Aptitud para principiantes 65/100
Codeinwp/wp-menu-icons#360 ·
-
[Type] Bug
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
Los mantenedores suelen responder en 5 días
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
Automattic/blocks-engine#4874 ·
Los mantenedores suelen responder en 1 día
-
Visual COBOL: missing 11.0 and 12.0, and 9.0 date differs from Rocket Software's lifecycle pageAbierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
endoflife-date/endoflife.date#11561 ·
Los mantenedores suelen responder en 1 día