Hi Internals,
I'd like to express my interest in a possible feature concerning weak
references. Currently closures created within a class appear to
contain a reference to the object that created it. This is of course
necessary in order for the closure to retain the necessary scope.
However I would like to suggest we have the option for closure to
weakly reference this object so that when the object is garbage
collected, the closure too may be rendered null or invalid
(inspectable).
Consider the following example:
https://gist.github.com/orolyn/7651e4127759aad1736547490baa1394
The idea here is that without unset($sample) the loop would run
forever, because there is a reference to the callback from the object,
and in turn there is a reference to the object from the main stack.
With the unset($sample), the idea is that before the callback is even
called, the reference to the object is destroyed thusly so is the
reference to callback, and since the callback is only referenced now
in a WeakMap, the callback will finally be gone.
In reality it appears there is a circular reference between the object
and the callback. I don't know if this is a bug or not, because from
what I can find PHP was fixed a long time ago to resolve circular
references.
However if this is intentional, I would like the option to make the
closure weak for example:
$c = WeakClosure::fromCallable(function () {});
$c = Closure::fromCallable(function () {}, true);
Please let me know your thoughts, because maybe there is a way of
achieving this with PHP as is.
Kind regards,
Dominic
I'd like to express my interest in a possible feature concerning weak
references. Currently closures created within a class appear to
contain a reference to the object that created it. This is of course
necessary in order for the closure to retain the necessary scope.
However I would like to suggest we have the option for closure to
weakly reference this object so that when the object is garbage
collected, the closure too may be rendered null or invalid
(inspectable).Consider the following example:
https://gist.github.com/orolyn/7651e4127759aad1736547490baa1394The idea here is that without unset($sample) the loop would run
forever, because there is a reference to the callback from the object,
and in turn there is a reference to the object from the main stack.
With the unset($sample), the idea is that before the callback is even
called, the reference to the object is destroyed thusly so is the
reference to callback, and since the callback is only referenced now
in a WeakMap, the callback will finally be gone.In reality it appears there is a circular reference between the object
and the callback. I don't know if this is a bug or not, because from
what I can find PHP was fixed a long time ago to resolve circular
references.However if this is intentional, I would like the option to make the
closure weak for example:$c = WeakClosure::fromCallable(function () {});
$c = Closure::fromCallable(function () {}, true);Please let me know your thoughts, because maybe there is a way of
achieving this with PHP as is.
If you trigger the garbage collector manually (i.e. call
gc_collect_cycles()
after unset($callback)), the loop terminates right
away. I'm not sure why it doesn't without manually triggering the GC.
Christoph
If you trigger the garbage collector manually (i.e. call
gc_collect_cycles()
after unset($callback)), the loop terminates right
away. I'm not sure why it doesn't without manually triggering the GC.
Most values are freed as soon as their refcount reaches zero, which
obviously won't happen for circular references, so an additional
algorithm is needed for those. This "cycle collection" algorithm is
relatively expensive, so doesn't run every time a possible candidate is
found, but only when a list of candidates reaches a particular
threshold. The algorithm is outlined in the manual, although it looks
like the constant 10000 mentioned there has been replaced by an adaptive
threshold (which can be inspected with gc_status()
):
https://www.php.net/manual/en/features.gc.collecting-cycles.php
If you measure memory usage while running the example code in a loop,
you should see it slowly growing and then periodically dropping each
time a cycle collection is run. That's why gc_collect_cycles()
exists -
if you know you've created circular references, you can tell the
engine to find and free them immediately, rather than waiting for the
next pass.
Regards,
--
Rowan Tommins
[IMSoP]
If you trigger the garbage collector manually (i.e. call
gc_collect_cycles()
after unset($callback)), the loop terminates right
away. I'm not sure why it doesn't without manually triggering the GC.Most values are freed as soon as their refcount reaches zero, which
obviously won't happen for circular references, so an additional
algorithm is needed for those. This "cycle collection" algorithm is
relatively expensive, so doesn't run every time a possible candidate is
found, but only when a list of candidates reaches a particular
threshold. The algorithm is outlined in the manual, although it looks
like the constant 10000 mentioned there has been replaced by an adaptive
threshold (which can be inspected withgc_status()
):
https://www.php.net/manual/en/features.gc.collecting-cycles.phpIf you measure memory usage while running the example code in a loop,
you should see it slowly growing and then periodically dropping each
time a cycle collection is run. That's whygc_collect_cycles()
exists -
if you know you've created circular references, you can tell the
engine to find and free them immediately, rather than waiting for the
next pass.
Ah, right. Thanks Rowan!
Christoph
That's very strange, I had thought it might be a bug. For simple use cases
where an object references it's own closure it might be a memory leak, but
for complex event loops it potentially allows unnecessary code to run
indefinitely.
I'm afraid I don't know any more than that. Thanks for the tip though.
I'd like to express my interest in a possible feature concerning weak
references. Currently closures created within a class appear to
contain a reference to the object that created it. This is of course
necessary in order for the closure to retain the necessary scope.
However I would like to suggest we have the option for closure to
weakly reference this object so that when the object is garbage
collected, the closure too may be rendered null or invalid
(inspectable).Consider the following example:
https://gist.github.com/orolyn/7651e4127759aad1736547490baa1394The idea here is that without unset($sample) the loop would run
forever, because there is a reference to the callback from the object,
and in turn there is a reference to the object from the main stack.
With the unset($sample), the idea is that before the callback is even
called, the reference to the object is destroyed thusly so is the
reference to callback, and since the callback is only referenced now
in a WeakMap, the callback will finally be gone.In reality it appears there is a circular reference between the object
and the callback. I don't know if this is a bug or not, because from
what I can find PHP was fixed a long time ago to resolve circular
references.However if this is intentional, I would like the option to make the
closure weak for example:$c = WeakClosure::fromCallable(function () {});
$c = Closure::fromCallable(function () {}, true);Please let me know your thoughts, because maybe there is a way of
achieving this with PHP as is.If you trigger the garbage collector manually (i.e. call
gc_collect_cycles()
after unset($callback)), the loop terminates right
away. I'm not sure why it doesn't without manually triggering the GC.Christoph
Hi Internals,
I'd like to express my interest in a possible feature concerning weak
references. Currently closures created within a class appear to
contain a reference to the object that created it. This is of course
necessary in order for the closure to retain the necessary scope.
However I would like to suggest we have the option for closure to
weakly reference this object so that when the object is garbage
collected, the closure too may be rendered null or invalid
(inspectable).Consider the following example:
https://gist.github.com/orolyn/7651e4127759aad1736547490baa1394The idea here is that without unset($sample) the loop would run
forever, because there is a reference to the callback from the object,
and in turn there is a reference to the object from the main stack.
With the unset($sample), the idea is that before the callback is even
called, the reference to the object is destroyed thusly so is the
reference to callback, and since the callback is only referenced now
in a WeakMap, the callback will finally be gone.In reality it appears there is a circular reference between the object
and the callback. I don't know if this is a bug or not, because from
what I can find PHP was fixed a long time ago to resolve circular
references.However if this is intentional, I would like the option to make the
closure weak for example:$c = WeakClosure::fromCallable(function () {});
$c = Closure::fromCallable(function () {}, true);Please let me know your thoughts, because maybe there is a way of
achieving this with PHP as is.Kind regards,
Dominic--
To unsubscribe, visit: https://www.php.net/unsub.php
Hi Dominic,
Implementing a weak closure is possible in user code. We've done so in Amp v3: https://github.com/amphp/amp/blob/379177aba93518e2df6d626677cbbdc48cc0d8ae/src/functions.php#L119-L171
I would be in favor of having this functionality available directly in PHP. Amp often uses references to object properties in static closures to avoid circular references to $this. There are other ways to accomplish this, such as a ref object like https://github.com/azjezz/psl/blob/21bf0cd3d6d6055fc88541e9b24f3140bd179b2d/src/Psl/Ref.php, but a weak-ref to $this would certainly be more convenient.
Cheers,
Aaron Piotrowski
Yes while experimenting I found that solution to work. In order to support
$this->func(...) Though I had to use reflection to separate the object and
method name.
On Jan 21, 2022, at 4:31 AM, Dominic Grostate <
codekestrel@googlemail.com> wrote:Hi Internals,
I'd like to express my interest in a possible feature concerning weak
references. Currently closures created within a class appear to
contain a reference to the object that created it. This is of course
necessary in order for the closure to retain the necessary scope.
However I would like to suggest we have the option for closure to
weakly reference this object so that when the object is garbage
collected, the closure too may be rendered null or invalid
(inspectable).Consider the following example:
https://gist.github.com/orolyn/7651e4127759aad1736547490baa1394The idea here is that without unset($sample) the loop would run
forever, because there is a reference to the callback from the object,
and in turn there is a reference to the object from the main stack.
With the unset($sample), the idea is that before the callback is even
called, the reference to the object is destroyed thusly so is the
reference to callback, and since the callback is only referenced now
in a WeakMap, the callback will finally be gone.In reality it appears there is a circular reference between the object
and the callback. I don't know if this is a bug or not, because from
what I can find PHP was fixed a long time ago to resolve circular
references.However if this is intentional, I would like the option to make the
closure weak for example:$c = WeakClosure::fromCallable(function () {});
$c = Closure::fromCallable(function () {}, true);Please let me know your thoughts, because maybe there is a way of
achieving this with PHP as is.Kind regards,
Dominic--
To unsubscribe, visit: https://www.php.net/unsub.php
Hi Dominic,
Implementing a weak closure is possible in user code. We've done so in Amp
v3:
https://github.com/amphp/amp/blob/379177aba93518e2df6d626677cbbdc48cc0d8ae/src/functions.php#L119-L171I would be in favor of having this functionality available directly in
PHP. Amp often uses references to object properties in static closures to
avoid circular references to $this. There are other ways to accomplish
this, such as a ref object like
https://github.com/azjezz/psl/blob/21bf0cd3d6d6055fc88541e9b24f3140bd179b2d/src/Psl/Ref.php,
but a weak-ref to $this would certainly be more convenient.Cheers,
Aaron Piotrowski