Why can memory leaking by cross-reference be solved by explicit reassignment in Perl?

Viewed 78

Cross-reference causes memory leaking in Perl like this.

{
    my @a = qw(a b c);
    my @b = qw(a b c);
    # both reference count are 1
    push @a, \@b;
    # @b reference count is 2(from @b and via @a)
    push @b, \@a;
}
# @b reference count is 2(from via @a)

I understand memory leaking by cross-reference in this situation. But the memory leaking can be resolve by explicit reassignment like this.

{
    my @a = qw(a b c);
    my @b = qw(a b c);
    # both reference count are 1
    push @a, \@b;
    # @b reference count is 2(from @b and via @a)
    push @b, \@a;
    @a = ();
}
# why is @b reference count 0?

@a is lexical scope so I think even if there is no reassignment, @a's reference will be invalid but former cause memory leaking and later is not, why?

1 Answers

You start with

@a                  @b
|   ARRAY           |   ARRAY
|   REFCNT=2        |   REFCNT=2
+-->+-----------+   +-->+-----------+
|   | +-------+ |   |   | +-------+ |
|   | | a     | |   |   | | a     | |
|   | +-------+ |   |   | +-------+ |
|   | | b     | |   |   | | b     | |
|   | +-------+ |   |   | +-------+ |
|   | | c     | |   |   | | c     | |
|   | +-------+ |   |   | +-------+ |
|   | |     --------+   | |     --------+
|   | +-------+ |       | +-------+ |   |
|   +-----------+       +-----------+   |
|                                       |
+---------------------------------------+

If you were to exit the scope here, the reference counts would drop to one, and they would leak.

After @a = ();:

@a                  @b
|   ARRAY           |   ARRAY
|   REFCNT=2        |   REFCNT=1
+-->+-----------+   +-->+-----------+
|   |           |       | +-------+ |
|   |           |       | | a     | |
|   |           |       | +-------+ |
|   |           |       | | b     | |
|   |           |       | +-------+ |
|   |           |       | | c     | |
|   |           |       | +-------+ |
|   |           |       | |     --------+
|   |           |       | +-------+ |   |
|   +-----------+       +-----------+   |
|                                       |
+---------------------------------------+

Note that @b's reference count went from two to one.

On scope exit, @a's reference count will drop to one, and @b's reference count will drop to zero.[1] This will free @b, which will cause @a's reference count to drop to zero. And that will free @a.

No cycle, so no memory leak.


  1. At least in theory. In practice, what actually happens is a bit different as an optimization. But those are internal details that aren't relevant here.
Related