【问题标题】:Is there a way to make the Jruby runtime intern all strings?有没有办法让 Jruby 运行时实习生成为所有字符串?
【发布时间】:2012-05-31 13:59:27
【问题描述】:

我们有一个在tomcat下运行的java/jruby webapp,我一直在分析应用程序在运行时的对象数量和内存使用情况。启动后我注意到“org.jruby.RubyString”类有1,118,000个字符串“”实例,仅空字符串使用的堆内存总量为65mb,这对我来说很荒谬,因为它是15%的内存由 webapp 使用。空字符串只是存在此问题的许多字符串值的一个示例,如果我可以实习我计算出的所有 jruby 字符串,我可以节省大约 130mb。

我知道在Java中,每次创建字符串值时,它都会检查该值是否已经存在于字符串池中,如果存在则重用它。我想知道 Jruby 中是否有具有相同优化的选项?如果是这样,我该如何启用它?

Jruby 中的示例:

v1 = "a"
v2 = "a"
puts v1.object_id # => 3352
puts v2.object_id # => 3354

Java 中的示例:

String v1 = "a";
String v2 = "a";

System.out.println(v1.hashCode()); # => 97
System.out.println(v2.hashCode()); # => 97

【问题讨论】:

  • 如果你用符号替换所有字符串,你会得到这种行为,但我不知道有一个选项可以自动使它成为实习字符串。
  • 不是一个理想的解决方案,因为很多这些字符串是从 3rd 方 gems 和插件创建的。
  • 你能发布一段产生这些空字符串的代码吗?
  • Java 哈希码并不能证明两个对象是相同的(并不是说您对字符串文字在 Java 中自动进行实习是错误的)。 String a = "a" + "b"; String b = "a" + "b"; AFAIK 将创建两个对象,而不是一个,但两者都将具有相同的哈希码。

标签: java ruby string optimization jruby


【解决方案1】:

我理解这背后的动机,但在 JRuby 中真的没有这种“神奇”的开关......

从 Java 的背景来看,保存字符串感觉很诱人,但您不能期望字符串在 JRuby 中的行为方式与它们在 Java 中的行为方式相同。首先,它们是完全不同的对象。我什至会说 Ruby String 更像是 Java StringBuilder

拥有如此多的"" 实例肯定是一种浪费,但如果您提到的代码是第三方代码,那么您无能为力 - 除非您喜欢猴子修补很多。我会尝试识别大多数实例的来源并重构它们 - 但请记住在保存字符串时有一些“棘手”的部分,例如Hash:

{ 'foo' => 'bar' }

你会猜这会创建 3 个对象,但你错了;它实际上创建了两个'foo'。因为String 是可变的(除非frozen?)它dups 字符串和freezes 当用作Hash 键时(这是有充分理由的)。

还请记住“智能”重构 - 如果您不通过尝试降低分配的实例成本来减慢速度,则分析您正在更改的位。

【讨论】:

  • 信息丰富的答案,但基本上可以归结为“jruby(可能是 ruby​​)字符串效率低下,所以忍受它。”虽然这可能是最后的决定,但这并不令人欣慰。
  • @Glenn 抱歉,但由于涉及到第 3 方代码,我猜真的没有更好的答案了。一些宝石已经认识到这一点并通过冻结并将它们存储在常量中来节省字符串,例如github.com/puma/puma/blob/master/lib/puma/const.rb,而不幸的是,其他人假设他们可以修改他们收到的String 方法参数。在某些时候,jruby(甚至是 MRI)可能会尝试重用一些 String“常量”,但我怀疑他们可以做很多事情,这主要留给我们程序员......
  • 问题真正归结为 Ruby have 为文字字符串(以及散列和数组)创建新对象,因为这些对象是可变的并且运行时没有办法知道它们不会变异。如果你打开了一些魔法开关并使字符串不可变,那么你正在使用的库可能会损坏。
【解决方案2】:
v1 = v2 = v3 = "a"

在 Ruby 中只会创建一个对象,而不是三个。

v1 = v2 = v3 = "a" # => "a"
v1.object_id # => 10530560
v2.object_id # => 10530560
v1 << "ll the same" # => "all the same"
v2 # "all the same"

在做像实习所有字符串这样激烈的事情之前,我会与其他 tomcat 用户核实这是否是处理此问题的最佳方法。我不使用 Tomcat 或 JRuby,但我强烈怀疑这不是最好的方法。

编辑 如果从“a”构建的每个对象都是同一个对象,那么修改其中一个将修改所有其他字符串。那将是一个副作用的噩梦。

【讨论】:

  • 我之前评论中的例子是一个懒惰的例子。试试,a = "a", b = "a" 两个对象会有不同的object_id。
【解决方案3】:

在 JRuby 中实习 String 的唯一方法是调用 to_symintern(它们互为别名),从而使它们成为符号 - 正如您所提到的,这对第三方没有太大帮助宝石。据我所知,没有任何其他方式。

这符合 MRI 行为:

sebastien@greystones:~$ rvm ruby-1.9.3-p0
sebastien@greystones:~$ irb
1.9.3p0 :001 > a = "Hello World" 
 => "Hello World" 
1.9.3p0 :002 > b = "Hello World"
 => "Hello World" 
1.9.3p0 :003 > a.object_id
 => 20126420 
1.9.3p0 :004 > b.object_id
 => 19289920 

【讨论】:

  • 即使调用#to_sym 也无济于事,因为此时已经创建了字符串对象。它必须是一个符号开头。
【解决方案4】:

这现在是 JRuby 中的默认行为。从 9.1 版开始,所有冻结的字符串文字(例如'hello'.freeze)都返回相同的实例,用作哈希键的文字字符串(例如stuff['thing'])和其他一些情况也是如此。见JRuby issue #3491

如果您想积极冻结所有字符串文字,您可以使用 --enable-frozen-string-literal 同时运行 JRuby (9.1+) 和 Ruby (2.3+),但要做好准备,因为大多数 gem 假定字符串是可变的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-08-08
    • 2020-11-05
    • 2016-06-01
    • 1970-01-01
    • 2022-06-12
    • 1970-01-01
    • 2020-11-20
    • 1970-01-01
    相关资源
    最近更新 更多