Thanks to visit codestin.com
Credit goes to github.com

Skip to content

@nullOut annotation #10959

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 2 commits into
base: 2.13.x
Choose a base branch
from
Draft

@nullOut annotation #10959

wants to merge 2 commits into from

Conversation

lrytz
Copy link
Member

@lrytz lrytz commented Dec 13, 2024

Includes changes from #10937.

Continuation of #7990.


this: @nullOut or local: @nullOut sets the local variable slot to
null after loading the value on the stack.

Mixin forwarders, static mixin accessors and bridges set the receiver
local to null if the target method is annotated @nullOut.


As a test, use @nullOut for the forwarder / alias IterableOnceOps./:.
It uses @nullOut to clear
the this reference before calling foldLeft. The annotation is
also on the method itself to ensure mixin forwarders (generated in
the subclass) and the static mixin accessor (generated in the trait)
null out the receiver before invoking /:.

After removing LazyList.foldLeft, it inherits the implementation
from LinearSeqOps. That method again uses @nullOut twice, once
to clear this when it's no longer needed, and once on the method
for the generated accessors.

I think we should not use @nullOut everywhere in the standard
library, only on methods that cannot be overridden in LazyList.
So, for final methods like /: or mkString.

@scala-jenkins scala-jenkins added this to the 2.13.17 milestone Dec 13, 2024
@SethTisue
Copy link
Member

😎🆒

@som-snytt
Copy link
Contributor

I don't understand Seth's emojis so ❤️ and 🚀.

@lrytz
Copy link
Member Author

lrytz commented Dec 18, 2024

I took another look at mkString, as the motivating example is Random.alphanumeric.take(bigNumber).mkString.

@nullOut works to prevent live this references on the stack through all the forwarders. But unfortunately, LazyList overrides addString (which is the actual implementation of mkString) to ensure cycles are detected.

scala> val ll: LazyList[Int] = 1 #:: 2 #:: 3 #:: ll
val ll: LazyList[Int] = LazyList(<not computed>)

scala> ll.mkString
val res0: String = 123<cycle>

Even though that could be considered an inconsistency, it seems desired (scala/collection-strawman#550, scala/bug#8680).

Cycle detection uses the 2x/1x iterator idea (Floyd's Algorithm). So for a very long non-cyclic LazyList, the slow pointer points to the middle of the list when the fast one reaches the end. So at least half of the list cannot be GC'd.

I don't see how we could fix it.

@lrytz lrytz force-pushed the lazy-list-null-out branch 2 times, most recently from 260cea2 to 8f785ec Compare December 18, 2024 16:29
@SethTisue
Copy link
Member

@-ing @NthPortal and @scala/collections

Copy link
Contributor

@NthPortal NthPortal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

neat! I don't know enough the compiler to be confident in saying anything about the implementation, but it look decent to me

@lrytz
Copy link
Member Author

lrytz commented Dec 19, 2024

Hello, optimizer!

https://github.com/scala/scala/blob/v2.13.15/src/compiler/scala/tools/nsc/backend/jvm/opt/CopyProp.scala#L143

...// current field value is `this`, which won't be gc'd anyway

@lrytz lrytz force-pushed the lazy-list-null-out branch from 8f785ec to d783f87 Compare December 19, 2024 13:29
@lrytz lrytz force-pushed the lazy-list-null-out branch 2 times, most recently from 8fd5c74 to 4fa73fe Compare January 30, 2025 21:00
lrytz and others added 2 commits January 31, 2025 08:47
`this: @nullOut` or `local: @nullOut` sets the local variable slot to
`null` after loading the value on the stack.

Mixin forwarders, static mixin accessors and bridges set the receiver
local to `null` if the target method is annotated `@nullOut`, for
example.

Co-authored-by: Stefan Zeiger <[email protected]>
Needs a restarr to work!

The forwarder / alias `IterableOnceOps./:` uses `@nullOut` to clear
the `this` reference before calling `foldLeft`. The annotation is
also on the method itself to ensure mixin forwarders (generated in
the subclass) and the static mixin accessor (generated in the trait)
null out the receiver before invoking `/:`.

After removing `LazyList.foldLeft`, it inherits the implementation
from LinearSeqOps. That method again uses `@nullOut` twice, once
to clear `this` when it's no longer needed, and once on the method
for the generated accessors.

I think we should *not* use `@nullOut` everywhere in the standard
library, only on methods that cannot be overridden in LazyList, like
`/:`.

`@nullOut` works correctly on `mkString` too, but unfortunately the
`addString` override in LazyList prevents GC of processed elements
due to cycle detection.
@lrytz lrytz force-pushed the lazy-list-null-out branch from 4fa73fe to a4216e5 Compare January 31, 2025 10:26
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.

5 participants