Skip to content

By-name params used only in a lazy field should be cleared to prevent leaks #1692

Closed
@Blaisorblade

Description

@Blaisorblade

Noticed in #1691 — scalac, but not dotty (as pointed out by @smarter), compiles the following so the initializer for byLazyValMsg clears byNameMsg. This is not just an optimization, but is needed for correctness to prevent memory leaks:

class TestByNameLazy(byNameMsg: => String) {
  lazy val byLazyValMsg = byNameMsg
}

Resulting code with Scalac (as decompiled with jad)—note byName = null;:

public class TestByNameLazy
{

    private String byLVal$lzycompute()
    {
        synchronized(this)
        {
            if(!bitmap$0)
            {
                byLVal = (String)byName.apply();
                bitmap$0 = true;
            }
            BoxedUnit _tmp = BoxedUnit.UNIT;
        }
        byName = null;
        return byLVal;
    }

    public String byLVal()
    {
        return bitmap$0 ? byLVal : byLVal$lzycompute();
    }

    public TestByNameLazy(Function0 byName)
    {
        this.byName = byName;
        super();
    }

    private final Function0 byName;
    private String byLVal;
    private volatile boolean bitmap$0;
}

Is this documented anywhere?

Maybe but not to my knowledge; I've seen similar patterns often enough and Dragos described this in scala/scala#922 as:

Less known fact: lazy values null-out their dependent values is they're accessed only from
their initializer.

I found that PR by searching JIRA https://issues.scala-lang.org/browse/SI-6092 (which is not about this "feature" missing altogether, just about a bug with it).

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions