8000 Suboptimal memory usage for zio.Atomic (inside zio.Ref) · Issue #9709 · zio/zio · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content
Suboptimal memory usage for zio.Atomic (inside zio.Ref) #9709
@maximskripnik

Description

@maximskripnik

Hello 👋

This issue is introduced in version 2.1.15 with #9500.

The root cause is this part:

override def toString: String = s"Ref.Atomic(initial = $initial)"

This usage of initial inside final class Atomic[A] prevents it from being garbage collected. The only other usage is inside val unsafe = new AtomicReference[A](initial), but that is mutable so if it wasn't for such toString implementation, it would have eventually be garbage collected after the referenced value is changed using any of the mutation methods.

It might not be a problem for cases when the initial value for Ref is not a large object, but in our case we happen to initiate it with very large values. The fact that those values are kept in memory even after the ref is modified, makes us waste gigabytes of memory with data that we will never access.

The previous toString version used to print the current value:

override def toString: String =
  s"Ref(${value.get})"

@hearnadam argues that this is unnecessary, which I can relate to, especially since it's not suspended. However, I think printing the initial value is arguably even less useful. It is possible to keep this behaviour it by memoizing initial.toString though:

private[zio] final class Atomic[A](initial: A) extends Ref[A] { self =>
  private val initialStr = initial.toString
  ...
  override def toString: String = s"Ref.Atomic(initial = $initialStr)"
  ...
}

I think it's fair to assume that in many cases the string representation of the initial will be considerably smaller than the actual value. But this way the initial value can be garbaged collected once the ref is modified, so there is much less memory usage. Alternatively, toString could be simplified even further and just not print any content of the wrapped value (initial or current). I guess the old behavior can also be brought back with usafe.get, though I agree with @hearnadam that it's not worth it

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      0