在分配内存/对象ID方面,Ruby JSON.parse如何与OJ.load不同

时间:2016-08-19 15:51:30

标签: json ruby memory-management

这是我的第一个问题,我已经尽力找到答案 - 我已经到处寻找答案,但在oj docs和ruby json docs以及这里找不到任何具体的答案。

Oj是一个用于提高序列化/反序列化速度的gem,可在以下位置找到:https://github.com/ohler55/oj

当我尝试转储并解析包含在其中的NaN的哈希值两次时,我注意到了这种差异,并将两者进行了比较,即

# Create json Dump
dump = JSON.dump ({x: Float::NAN})
# Create first JSON load
json_load = JSON.parse(dump, allow_nan: true)
# Create second JSON load
json_load_2 = JSON.parse(dump, allow_nan: true)
# Create first OJ load
oj_load = Oj.load(dump, :mode => :compat)
# Create second OJload
oj_load_2 = Oj.load(dump, :mode => :compat)

json_load == json_load_2 # Returns true
oj_load == oj_load_2 # Returns false

我一直认为NaN无法与NaN进行比较所以这让我困惑了一段时间,直到我意识到json_load和json_load_2具有相同的对象ID而oj_load和oj_load_2没有。

有人能指出我的内存分配/对象ID分配发生的方向,或者我如何用OJ控制这种行为?

非常感谢和抱歉,如果这个答案在互联网上的某个地方浮动,我找不到它。

其他信息: 我正在运行Ruby 1.9.3。

以下是我的测试对象ID的输出:

puts Float::NAN.object_id; puts JSON.parse(%q({"x":NaN}), allow_nan: true)["x"].object_id; puts JSON.parse(%q({"x":NaN}), allow_nan: true)["x"].object_id
70129392082680
70129387898880
70129387898880

puts Float::NAN.object_id; puts Oj.load(%q({"x":NaN}), allow_nan: true)["x"].object_id; puts Oj.load(%q({"x":NaN}), allow_nan: true)["x"].object_id    
70255410134280
70255410063100
70255410062620

也许我做错了什么?

1 个答案:

答案 0 :(得分:2)

我认为这是一个深层实施细节Oj does this

if (ni->nan) {
  rnum = rb_float_new(0.0/0.0);
}

我无法找到相应的Ruby,Float.new似乎不存在,但确实每个都创建一个新的Float对象时间(来自实际C的现场构建的NaN),因此不同object_id s。

Ruby's JSON module uses (also in C) its own JSON::NaN Float object everywhere

CNaN = rb_const_get(mJSON, rb_intern("NaN"));

这解释了为什么你会得到不同的NaN s' object_id与Oj相同,与Ruby的JSON相同。

无论生成的哈希值是object_id,问题都出在NaN上。如果它们具有相同的object_id s,则封闭的哈希值被认为是相等的。如果没有,他们就不是。

根据文档,当{且仅当参数相同的对象时,Hash#==使用Object#==仅输出true的值(同一{{} 1}})。这与NaN不等于自身的属性相矛盾。

壮观。继承已经过去了。

可能会修改Oj的C代码(甚至用它做一个拉取请求)来使用像Ruby的JSON模块那样的常量。这是一个微妙的变化,但我认为这是object_id的精神。