Skip to content
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

Memory leak with futures #880

Open
HadrienRenaud opened this issue Jul 2, 2020 · 2 comments
Open

Memory leak with futures #880

HadrienRenaud opened this issue Jul 2, 2020 · 2 comments

Comments

@HadrienRenaud
Copy link

It seems that creating and accessing a future create some non reachable objects.

Minimal example

Tested on branch development at a98629f.

With the following example, the usage of memory explodes, and quickly fills all the memory available (more than 16GB).

active class A
  def foo(): int
    1
  end
end

active class Main
  def main() : unit
    val a = new A
    val n = 10000000
    repeat i <- n do
      get(a!foo())
    end
  end
end

For an example with memory usage display, you can use this gist. This behavior does not happen with local objects (ie without futures).

First analysis

We suspect that this behaviour is due to some poor garbage collection while doing the get. Precisely, at runtime, the encore.c function actor_save_context that is used by get while blocking the actor, creates a context with pop_context (which internally calls malloc) and this context does not seem to be properly handled.

Valgrind summary:

==32075== LEAK SUMMARY:                                                         
==32075==    definitely lost: 3,776 bytes in 4 blocks                           
==32075==    indirectly lost: 102,400 bytes in 1 blocks                         
==32075==      possibly lost: 307,200 bytes in 3 blocks                         
==32075==    still reachable: 9,827 bytes in 9 blocks                           
==32075==         suppressed: 0 bytes in 0 blocks 
@HadrienRenaud
Copy link
Author

@moy @amaurymaille @lhenrio

@albertnetymk
Copy link
Contributor

The unbounded mem growth is not related to get. val fut = a!foo() still exposes the same behavior. The underlying reason is that Encore only does GC between msg processing. IOW, the mem growth is caused by all the futures generated by a!foo().

Rewriting it in tail-call fashion will give you more or less constant mem usage, because those futures can be collected between msgs.

active class Main
  var a : A
  def main() : unit
    this.a = new A
    val n = 10000000
    this!f(n)
  end

  def f(n:int) : unit
    if n == 0 then
      ()
    else
      get(this.a!foo())
      this!f(n-1)
    end
  end
end

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

No branches or pull requests

2 participants