克隆ruby中的字符串

时间:2017-07-08 14:35:29

标签: ruby string memory clone

我的问题是:如何克隆 ruby​​

什么时候发生克隆?

所以,我所有的功能是 cur_memory ,它返回 Gb 中当前使用的内存,字符串 s1 包含长文本和 s2 克隆为prev:

def cur_memory
  %x(free).split("\n")[1].split(' ')[2].to_f / 10**6
end

请注意,它需要3 GB的内存! (或者您可以在下面用8替换9)

m = cur_memory              # snapshot
s1 = '0'*10**9              # create long string

p (cur_memory - m).round 3  # 1st output
s2 = s1.clone
p (cur_memory - m).round 3  # 2nd output
s1[0] = '2'
p (cur_memory - m).round 3  # 3rd output
s2[0] = '1'
p (cur_memory - m).round 3  # 4th output

输出:

0.978
0.978
1.957
2.936
  1. 首先输出没问题!
  2. 第一次输出后为什么没有变化?
  3. 好吧,也许红宝石聪明,也许它是一种懒惰的克隆。 (但无论如何,我想知道)
  4. 但WTF呢? 3千兆字节而不是2千字节
  5. P.S。在Ruby 2.1.0和2.4.0上测试

1 个答案:

答案 0 :(得分:0)

我在MacOS X上的MRI 2.4和JRuby 9.1.2中对此进行了测试。在MRI和JRuby中,我发现它们在作为脚本运行时表现相似。当原始版本或副本被修改时,即为它们不再匹配时,将为克隆分配内存。

def cur_memory
  pid = Process.pid
  `ps -p#{pid} -o rss=`.to_f / 1024
end

def diff(m)
  (cur_memory - m).round 3
end

m = cur_memory              # snapshot

s1 = '0'*10**8              # create long string
p "1st output #{diff(m)} MB"

s2 = s1.clone
p "2nd output #{diff(m)} MB"

s2[0] = '2'
p "3rd output #{diff(m)} MB"

s1[0] = '1'
p "4th output #{diff(m)} MB"

s2[0] = '1'
p "5th output #{diff(m)} MB"

s1[0] = '2'
p "6th output #{diff(m)} MB"

输出:

"1st output 95.379 MB"
"2nd output 95.41 MB"
"3rd output 190.781 MB"
"4th output 286.152 MB"
"5th output 286.152 MB"
"6th output 286.152 MB"

在JRuby中,我们可以观察堆内存的分配方式。它表明,一旦分配了克隆,修改就不会增加堆的使用。

require 'java'

java_import 'java.lang.System'
java_import 'java.lang.management.ManagementFactory'

def cur_heap
  ManagementFactory.getMemoryMXBean.getHeapMemoryUsage.getUsed.to_f / (1024 * 1024)
end

def cur_memory
  pid = Process.pid
  `ps -p#{pid} -o rss=`.to_f / 1024
end

def diff_mem(m)
  (cur_memory - m).round 3
end

def diff_heap(m)
  (cur_heap - m).round 3
end

System.gc
m = cur_memory              # snapshot
h = cur_heap

s1 = '0'*10**8              # create long string
System.gc
p "1st output #{diff_heap(h)}/#{diff_mem(m)} MB"

s2 = s1.clone
System.gc
p "2nd output #{diff_heap(h)}/#{diff_mem(m)} MB"

s2[0] = '2'
System.gc
p "3rd output #{diff_heap(h)}/#{diff_mem(m)} MB"

s1[0] = '1'
System.gc
p "4th output #{diff_heap(h)}/#{diff_mem(m)} MB"

s2[0] = '1'
System.gc
p "5th output #{diff_heap(h)}/#{diff_mem(m)} MB"

s1[0] = '2'
System.gc
p "6th output #{diff_heap(h)}/#{diff_mem(m)} MB"

输出:

"1st output 95.392/103.844 MB"
"2nd output 94.503/101.262 MB"
"3rd output 189.871/200.262 MB"
"4th output 189.876/299.277 MB"
"5th output 189.877/299.328 MB"
"6th output 189.878/299.41 MB"

注意:使用IRB时,MRI行为略有不同。它会在创建克隆后立即显示内存使用量增加。