Skip to content

ActorGraphInterpreter retains reference to initial GraphInterpreterShell after shutdown #3243

Description

@He-Pin

Description

ActorGraphInterpreter holds a strong reference to its initial GraphInterpreterShell via the _initial constructor val. When the actor outlives the initial shell (e.g., while hosting subfused interpreters registered via registerShell), the initial shell and all its GraphStageLogic instances cannot be garbage collected for the actor's lifetime.

Affected code

stream/src/main/scala/org/apache/pekko/stream/impl/fusing/ActorGraphInterpreter.scala:

  • Line 732: _initial is a constructor val (not a var), stored as a private final field
  • Line 792: preStart() calls tryInit(_initial) but never releases the reference
  • Line 848: postStop() clears activeInterpreters but does not null out _initial

Reproduction scenario

The SubFusingActorMaterializerImpl (line 739) reuses the same ActorGraphInterpreter actor to host additional shells via registerShell (line 768). When the initial shell's stream completes, it is removed from activeInterpreters (line 822), but _initial still holds a reference to it. Any operator that uses subfusing (e.g., flatMapConcat, PrefixAndTail-derived operators) will trigger this pattern.

Impact

In long-running streams with subfused operators, the initial shell and all its graph stage logics remain pinned in memory for the actor's entire lifetime, even after the initial stream completes. This is a memory leak in subfusing scenarios.

Suggested fix

  1. Convert _initial from a constructor val to private var _initial
  2. Set _initial = null at the end of preStart() after tryInit completes
  3. Update any references to _initial in debug logging to use the shell parameter instead

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions