-
Notifications
You must be signed in to change notification settings - Fork 8
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 when a generator is created inside a function #36
Comments
I dug a little bit into the issue, it seems to be related to the implementation of the "get next" code. gtor <- coro::generator(function() {
long_vec <- sample(1:1e7) # large object to easily verify the leak
for (x in long_vec) {
coro::yield(x)
}
})
g <- gtor()
# memory leaks when g() is called more than once
g()
#> [1] 1957623
g()
#> [1] 737927
for (i in 1:10) gc(); lobstr::mem_used()
#> 86,089,560 B
g = 0
for (i in 1:10) gc(); lobstr::mem_used()
#> 86,090,328 B
g <- gtor()
# doesn't leak when g() is called only once
g()
#> [1] 117777
for (i in 1:10) gc(); lobstr::mem_used()
#> 85,254,592 B
g = 0
for (i in 1:10) gc(); lobstr::mem_used()
#> 45,469,496 B |
Thanks for investigating. I'll look into a quick coro release after rlang 1.0 is out. |
The leak occurred through an environment inlined in the body of generator instances. When the JIT compiles a function, it caches the bytecode in a hash table. The inlined environment was included in the constant pool of the bytecode and leaked through that cache. To fix this, we now inline a weak reference to the environment instead. memtools failed to detect the leak because bytecode objects are not currently traversed. I've opened r-lib/memtools#1 to track this. |
Thanks @lionel- ! That was tricky, I think I would never be able to figure this out :) |
No worries @dfalbel |
Thanks. Just learn something today. f <- function() {invisible(NULL)}
# put a large vector inline
body(f)[[2]][[2]] <- sample(1e7)
# run f two times to tigger JIT compilation
f()
f()
for (i in 1:10) gc(); lobstr::mem_used()
#> 89,809,128 B
rm(f)
for (i in 1:10) gc(); lobstr::mem_used() # memory leak
#> 90,066,024 B Created on 2021-12-04 by the reprex package (v2.0.1) |
It looks like that the function environment where the generator is created and used is never released, even if the generator itself is not in scope anymore.
Here's a reprex:
The text was updated successfully, but these errors were encountered: