From winstonchang1 at gmail.com  Tue Mar 27 05:49:33 2018
From: winstonchang1 at gmail.com (Winston Chang)
Date: Mon, 26 Mar 2018 22:49:33 -0500
Subject: [Rd] Objects not gc'ed due to caching (?) in R's S3 dispatch
 mechanism
In-Reply-To: <CALEXWq0gGFFf-EN5tUhgJpqF_Z5=1AAV-rokCLbEUxiWg_MjqA@mail.gmail.com>
References: <CALEXWq0gGFFf-EN5tUhgJpqF_Z5=1AAV-rokCLbEUxiWg_MjqA@mail.gmail.com>
Message-ID: <CAFOpNVFsNiQgy2pf2kWahx9NAQGYAmYz400hvkwG+FZs5P3WGA@mail.gmail.com>

I'd like to emphasize that although I?aki's example uses print(), it
also happens with other S3 generics. Please note that each of the
following examples might need to be run in a clean R session to work.

===========
Here's an example that doesn't use S3 dispatch. The finalizer runs correctly.

ident <- function(x) invisible(x)

env_with_finalizer <- function() {
  reg.finalizer(environment(), function(e) message("Finalizer called"))
  environment()
}

ident(env_with_finalizer())
gc() # Still in .Last.value
gc() # Finalizer called


===========
Here's an example that uses S3. In this case, the finalizer doesn't run.

ident <- function(x) UseMethod("ident")
ident.default <- function(x) invisible(x)

env_with_finalizer <- function() {
  reg.finalizer(environment(), function(e) message("Finalizer called"))
  environment()
}

ident(env_with_finalizer())
gc()
gc() # Nothing

However, if the S3 generic is called with another object, the
finalizer will run on the next GC:

ident(1)
gc() # Finalizer called

===========

This example is the same as the previous one, except that, at the end,
instead of calling the same S3 generic on a different object (that is,
ident(1)), it calls a _different_ S3 generic on a different object
(mean(1)).

ident <- function(x) UseMethod("ident")
ident.default <- function(x) invisible(x)

env_with_finalizer <- function() {
  reg.finalizer(environment(), function(e) message("Finalizer called"))
  environment()
}

ident(env_with_finalizer())
gc()
gc() # Nothing

# Call a different S3 generic
mean(1)
gc() # Finalizer called


-Winston

On Mon, Mar 26, 2018 at 4:46 PM, I?aki ?car <i.ucar86 at gmail.com> wrote:
> Hi,
>
> I initially opened an issue in the R6 repo because my issue was with
> an R6 object. But Winston (thanks!) further simplified my example, and
> it turns out that the issue (whether a feature or a bug is yet to be
> seen) had to do with S3 dispatching.
>
> The following example, by Winston, depicts the issue:
>
> print.foo <- function(x, ...) {
>   cat("print.foo called\n")
>   invisible(x)
> }
>
> new_foo <- function() {
>   e <- new.env()
>   reg.finalizer(e, function(e) message("Finalizer called"))
>   class(e) <- "foo"
>   e
> }
>
> new_foo()
> gc() # still in .Last.value
> gc() # nothing
>
> I would expect that the second call to gc() should free 'e', but it's
> not. However, if we call now *any* S3 method, then the object can be
> finally gc'ed:
>
> print(1)
> gc() # Finalizer called
>
> So the hypothesis is that there is some kind of caching (?) mechanism
> going on. Intended behaviour or not, this is something that was
> introduced between R 3.2.3 and 3.3.2 (the first succeeds; from the
> second on, the example fails as described above).
>
> Regards,
> I?aki
>
> PS: Further discussion and examples in https://github.com/r-lib/R6/issues/140
>
> ______________________________________________
> R-devel at r-project.org mailing list
> https://stat.ethz.ch/mailman/listinfo/r-devel



[Rd] Objects not gc'ed due to caching (?) in R's S3 dispatch mechanism
Winston Chang