Ruby 获取共享阵列空间的memsize tl;博士

Ruby 获取共享阵列空间的memsize tl;博士,ruby,memory-management,Ruby,Memory Management,它去哪里了 较长版本 数组中的一大堆东西似乎都有“共享数组”的概念,其中数据块被移动到共享堆空间。我知道memsize\u of清楚地表明它可能不完整,但是否有(好的)方法来分析这些共享数组块的分配?从ObjectSpace的角度来看,它们似乎不是“对象”。每个对象都是。对于这个内存分析器来说,即使我无法跟踪到特定对象,至少能够跟踪共享数组堆空间的总体大小也很好。幸运的是,rb\u ary\u memsize是一个公共函数,因此使用小技巧,您可以做到: require 'objspace' O

它去哪里了

较长版本
数组中的一大堆东西似乎都有“共享数组”的概念,其中数据块被移动到共享堆空间。我知道
memsize\u of
清楚地表明它可能不完整,但是否有(好的)方法来分析这些共享数组块的分配?从
ObjectSpace的角度来看,它们似乎不是“对象”。每个对象都是
。对于这个内存分析器来说,即使我无法跟踪到特定对象,至少能够跟踪共享数组堆空间的总体大小也很好。

幸运的是,
rb\u ary\u memsize
是一个公共函数,因此使用小技巧,您可以做到:

require 'objspace'

ObjectSpace.memsize_of([0] * 1_000_000)
#=> 8000040
ObjectSpace.memsize_of(Array.new([0] * 1_000_000))
#=> 40
并加载到流程中,替换原始功能:

gcc $(ruby -rrbconfig \
  -e'puts RbConfig::CONFIG.values_at("rubyhdrdir","rubyarchhdrdir").map{|d| " -I#{d}"}.join') \
  -Wall -fpic -shared -o ary_memsize_hack.so ary_memsize_hack.c
它将产生所需的输出:

LD_PRELOAD="$(pwd)/ary_memsize_hack.so" ruby -robjspace \
  -e 'p ObjectSpace.memsize_of([0] * 1_000_000); 
      p ObjectSpace.memsize_of(Array.new([0] * 1_000_000))'
更新
rb_ary_memsize
函数,该函数负责估计数组大小,仅对拥有堆(即未共享和未嵌入)的数组执行此操作,否则返回零。一般来说,这是有意义的,因为如果您要计算应用程序中所有数组的大小,最终数字应该匹配,而使用我的补丁,共享数组的内容将被计数多次。我想主要的问题是如何在ruby端构造包装数组:本质上,内部数组上的引用丢失了,应用程序代码无法访问,看起来不可数。我的补丁只演示了如何到达共享阵列的根以公开大小,但我认为不应该以任何方式将其集成到上游。嵌入式阵列也会出现类似的问题,因为ruby还返回0作为大小,这并没有显示应用程序希望看到的内容:

8000040
8000040

ObjectSpace.memsize\u of(([0]*1\u 000\u 000).dup)
(或
clone
)也返回
40
。这可能是答案的一部分:
ObjectSpace.dump
会告诉您数组是共享的,并给您一个我认为是底层共享分配的指针,但是你不能把它变成可以交给
memsize\u的东西。可能足够(再加上知道大小)来估计共享阵列内存使用情况。更复杂的是,如果您试图测量某个特定代码块的内存使用情况,可以让一个数组对象指向在块之前创建的共享缓冲区,因此,我需要使用我认为相同的
参考
指针检查最早的一代。顺便说一句,为什么要跟踪这些共享数组?我正在为Chef开发一个内存分析器工具,因为Chef的内部数据结构是如何工作的,大多数数组都被封装在相当于
数组的文件中。新增
,因此可能触发共享堆重定位行为。我可以忽略它,因为这是一个最好的估计方法,但是跟踪它会很好。你能补充更多的解释为什么它会发生在第一位吗?还有没有比重新编译我们自己的版本更简单的方法?可能是
Fiddle
?我已经用澄清更新了答案,不幸的是我以前没有使用
Fiddle
API,可能会更简单,但是使用
LD_PRELOAD
会更健壮。另外,我认为关于共享数组的ruby Behaviorr是正确的。应用程序应该真正创建共享数组,目的是共享数组内容,而不是包装它。
8000040
8000040
require 'objspace'

puts ObjectSpace.dump([1])
#=> {"address":"0x000008033f9bd8", "type":"ARRAY", "class":"0x000008029f9a98", "length":1, 
#    "embedded":true, "memsize":40, "flags":{"wb_protected":true}}
puts ObjectSpace.dump([1, 2])
#=> {"address":"0x000008033f9b38", "type":"ARRAY", "class":"0x000008029f9a98", "length":2, 
#    "embedded":true, "memsize":40, "flags":{"wb_protected":true}}
puts ObjectSpace.dump([1, 2, 3])
#=> {"address":"0x000008033f9ac0", "type":"ARRAY", "class":"0x000008029f9a98", "length":3, 
#    "embedded":true, "memsize":40, "flags":{"wb_protected":true}}
puts ObjectSpace.dump([1, 2, 3, 4])
#=> {"address":"0x000008033f9a48", "type":"ARRAY", "class":"0x000008029f9a98", "length":4,
#    "memsize":72, "flags":{"wb_protected":true}}