[#114348] [Ruby master Feature#19832] Method#destructive?, UnboundMethod#destructive? — "sawa (Tsuyoshi Sawada) via ruby-core" <ruby-core@...>

Issue #19832 has been reported by sawa (Tsuyoshi Sawada).

15 messages 2023/08/06

[#114365] [Ruby master Bug#19834] Segmentation fault while running in docker — "ramachandran@... (Ramachandran A) via ruby-core" <ruby-core@...>

Issue #19834 has been reported by ramachandran@mallow-tech.com (Ramachandran A).

7 messages 2023/08/09

[#114380] [Ruby master Bug#19837] Concurrent calls to Process.waitpid2 misbehave on Ruby 3.1 & 3.2 — "kjtsanaktsidis (KJ Tsanaktsidis) via ruby-core" <ruby-core@...>

Issue #19837 has been reported by kjtsanaktsidis (KJ Tsanaktsidis).

7 messages 2023/08/11

[#114399] [Ruby master Feature#19839] Need a method to check if two ranges overlap — "shouichi (Shouichi KAMIYA) via ruby-core" <ruby-core@...>

Issue #19839 has been reported by shouichi (Shouichi KAMIYA).

27 messages 2023/08/18

[#114410] [Ruby master Bug#19841] Marshal.dump stack overflow with recursive Time — "segiddins (Samuel Giddins) via ruby-core" <ruby-core@...>

Issue #19841 has been reported by segiddins (Samuel Giddins).

9 messages 2023/08/18

[#114422] [Ruby master Feature#19842] Intorduce M:N threads — "ko1 (Koichi Sasada) via ruby-core" <ruby-core@...>

Issue #19842 has been reported by ko1 (Koichi Sasada).

30 messages 2023/08/21

[#114590] [Ruby master Bug#19857] Eval coverage is reset after each `eval`. — "ioquatix (Samuel Williams) via ruby-core" <ruby-core@...>

Issue #19857 has been reported by ioquatix (Samuel Williams).

21 messages 2023/08/30

[ruby-core:114464] [Ruby master Bug#19436] Call Cache for singleton methods can lead to "memory leaks"

From: "byroot (Jean Boussier) via ruby-core" <ruby-core@...>
Date: 2023-08-23 14:20:08 UTC
List: ruby-core #114464
Issue #19436 has been updated by byroot (Jean Boussier).


This bug is unrelated to YJIT, it happens with or without YJIT enabled. It thus cannot explain what you are seeing.

As for memory usage with YJIT, what you are describing isn't out of the ordinary. The default `--yjit-exec-mem-size` is `64MB`, and the rule of thumb is that if you had the metadata YJIT need to track, you will find an overhead of ~4x `yjit-exec-mem-size`, so roughly 250MB.

So you probably want to raise your Unicorn memory limit a bit more, or reduce the `--yjit-exec-mem-size`.

----------------------------------------
Bug #19436: Call Cache for singleton methods can lead to "memory leaks"
https://bugs.ruby-lang.org/issues/19436#change-104230

* Author: byroot (Jean Boussier)
* Status: Closed
* Priority: Normal
* Assignee: ko1 (Koichi Sasada)
* Target version: 3.3
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN
----------------------------------------
Using "memory leaks" with quotes, because strictly speaking the memory isn't leaked, but it can nonetheless lead to large memory overheads.

### Minimal Reproduction

```ruby
module Foo
  def bar
  end
end

def call_bar(obj)
  # Here the call cache we'll keep a ref on the method_entry
  # which then keep a ref on the singleton_class, making that
  # instance immortal until the method is called again with
  # another instance.

  # The reference chain is IMEMO(callcache) -> IMEMO(ment) -> ICLASS -> CLASS(singleton) -> OBJECT
  obj.bar
end

obj = Object.new
obj.extend(Foo)

call_bar(obj)

id = obj.object_id
obj = nil

4.times { GC.start }
p ObjectSpace._id2ref(id)
```

### Explanation

Call caches keep a strong reference onto the "callable method entry" (CME), which itself keeps a strong reference on the called object class
and in the cache of a singleton class, it keeps a strong reference onto the `attached_object` (instance).

This means that any call site that calls a singleton method, will effectively keep a strong reference onto the last receiver.
If the method is frequently called it's not too bad, but if it's infrequently called, it's effectively a (bounded) memory leak.
And if the `attached_object` is big, the wasted memory can be very substantial.

### Practical Implications

Once relative common API impacted by this is [Rails' `extending` API](https://api.rubyonrails.org/classes/ActiveRecord/QueryMethods.html#method-i-extending). This API allow to extend a "query result set" with a module.
These query results set can sometimes be very big, especially since they keep references to the instantiated `ActiveRecord::Base` instances etc.

### Possible Solutions

#### Only keep a weak reference to the CME

The fairly "obvious" solution is to keep a weak reference to the CME, that's what I explored in https://github.com/ruby/ruby/pull/7272, and it seems to work.
However in debug mode It does fail on an assertion during compaction, but it's isn't quite clear to me what the impact is.

Additionally, something that makes me think this would be the right solution, is that call caches already try to avoid marking the class:

```c
# vm_callinfo.h:275

struct rb_callcache {
    const VALUE flags;

    /* inline cache: key */
    const VALUE klass; // should not mark it because klass can not be free'd
                       // because of this marking. When klass is collected,
                       // cc will be cleared (cc->klass = 0) at vm_ccs_free().
```

So it appears that the class being also marked through the CME is some kind of oversight?

#### Don't cache based on some heuristics

If the above isn't possible or too complicated, an alternative would be to not cache CMEs found in singleton classes, except if it's the the singleton class of a `Class` or `Module`.

It would make repeated calls to such methods slower, but the assumption is that it's unlikely that these CME would live very long.

#### Make `Class#attached_object` a weak reference

Alternatively we could make the `attached_object` a weak reference, which would drastically limit the amount of memory that may be leaked in such scenario.

The downside is that `Class#attached_object` was very recently exposed in Ruby 3.2.0, so it means changing its semantic a bit.


cc @peterzhu2118 @ko1



-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

In This Thread

Prev Next