【问题标题】:stack level too deep (SystemStackError) when loading hash via require通过 require 加载哈希时堆栈级别太深(SystemStackError)
【发布时间】:2014-01-13 00:20:46
【问题描述】:

我有一个 ruby​​ 脚本,仅包含一个哈希表,它是由另一个脚本通过使用 PP.pp(mediacontent,filehandle) 将其转储到文件中编写的。

生成此哈希表的脚本通过逐页从供应商下载数据、解析 XML 并将所需对象转换为此哈希表来生成此哈希表,该哈希表会随着脚本下载更多数据而增长。

转储看起来像这样,前 2 1/2 个对象显示:

文件:mediacontent.rb

# encoding: utf-8

$mediacontent =
{"099923045533"=>
  {"obj"=>
    {"external_id"=>"099923045533",
     "media_type_id"=>5,
     "active"=>true,
     "name"=>"F.D.B.",
     "duration"=>286,
     "published_at"=>"2013-05-29T20:05:11Z",
     "properties"=>
      {"host"=>"Vevo",
       "host_url"=> "http://example.com/sample.m3u8",
       "favorites"=>"0",
       "views"=>"8495",
       "trending"=>"298",
       "popular"=>"1401",
       "copyright"=>"2013 Hustle Gang/Grand Hustle/eOne Music",
       "src_img_path"=>"http://example.com/xyz.jpg",
       "src_img_dim"=>"1241x697"}},
   "data"=>{}},
 "AEA040800053"=>
  {"obj"=>
    {"external_id"=>"AEA040800053",
     "media_type_id"=>5,
     "active"=>true,
     "name"=>"Aadi",
     "duration"=>351,
     "published_at"=>"2009-10-02T00:00:00Z",
     "properties"=>
      {"host"=>"Vevo",
       "host_url"=>"http://example.com/sample2.m3u8",
       "favorites"=>"1",
       "views"=>"20160",
       "trending"=>"0",
       "popular"=>"0",
       "copyright"=>"EMI Music Arabia",
       "src_img_path"=>"http://example.com/sample2.jpg",
       "src_img_dim"=>"640x339"}},
   "data"=>{}},
 "AEAB20400094"=>
  {"obj"=>
    {"external_id"=>"AEAB20400094",
     "media_type_id"=>5,
      etc.
    },
   "data"=>{}},

   etc...

}

包含其他哈希的相当标准的 ruby​​ 哈希。

此列表中有 75,000 个主键。

当我尝试执行以下操作时:

ruby mediacontent.rb

或在脚本中

require "./mediacontent.rb"

我得到了错误

mediacontent.rb:0: stack level too deep (SystemStackError)

如果我从该表中删除大约 30,000 个条目,该错误就会消失。我已经验证没有无休止的嵌套。每个主键都是一个包含嵌套哈希“obj”、“properties”和“data”的哈希。就是这样。

我觉得奇怪的是,生成这个表的脚本在将它转储出来之前在内部构建这个大哈希表没有问题。如果脚本的执行被中断并且必须重新运行,则该脚本应该(通过要求)将其读回。在哈希表增长到 75,000 个项目之前,这也能正常工作。

我正在运行 ruby​​ 1.9.3p484。没有导轨。只是红宝石。

【问题讨论】:

  • This 表示 Array#hash 返回一个 Fixnum,this 告诉您平台中 Fixnum 的大小(以字节为单位)。
  • @JustinWood - 第二个链接不会转到页面上的任何锚点。如果您谈论的是 size 属性,那么如果 Ruby 甚至拒绝加载散列,就不可能调用它。看来我已经遇到了对象大小的最大限制。源文件大约 52 兆字节。如果我将它分成 2 个单独的哈希表,它们加载得很好。如果是大小限制,那么 Ruby 错误消息“堆栈级别太深”就不太适合显示了。
  • 我这里有很深的递归,你自己写脚本吗?
  • @majioa - 我自己编写了脚本,没有任何递归。该脚本非常基本:下载一个 XML 文件,解析它,将数据对象保存在哈希表中,然后重复。每 1000 个对象,将哈希表保存到一个单独的 Ruby 源文件。如果脚本被中断,它会在下次运行时需要包含散列的源文件,并从中断处继续。在哈希表超过一定大小之前,这种方法运行良好。这似乎是 Ruby 的一个错误(或功能?),它要么限制哈希表的大小,要么限制源文件的大小,不确定是哪个。
  • 好的,尝试使用ulimit 将堆栈大小增加两次,例如增加到 16384 kbytes 为:ulimit -s 16384

标签: ruby hash hashtable stack-overflow


【解决方案1】:

经过一些实验,这是我发现的:

  • 问题不在于包含的 Ruby 源文件的长度 哈希表(目前超过 50 MB)。
  • 问题在于 Ruby 对源文件中哈希表的大小施加了限制。如果在脚本运行时构建哈希表,它可能会更大,但从源文件解析时允许的大小有限制。

这很好,将哈希表一分为二,并将它们放入一个数组中:

$mediacontent = []
$mediacontent << { (hash table of 37,500 items) }
$mediacontent << { (remaining 37,500 items ) }

基于此,我必须得出结论,Ruby 的错误消息是错误的,或者至少是误导性的。问题不在于“堆栈级别太深”。错误消息应该更像“对象太大”。

【讨论】:

    【解决方案2】:

    您可以尝试为您的环境增加堆栈的大小。

    echo "ulimit -s 40000" >> ~/.bashrc
    source ~/.bashrc
    

    然后再试一次。

    假设您使用的是 bash,它可能是 .bash_profile、.profile 或您的特定 shell 使用的任何内容,但您应该了解基本概念。

    如果生成此哈希的系统上的堆栈大小不同,这可以解释为什么它不会引发相同的问题。

    【讨论】:

    • 我试过的每个环境都会出现错误:Mac,Windows下的Linux VM。不过感谢您的提示。我仍然认为这是 Ruby 解析源文件中的大对象时产生的错误消息的缺陷。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-10
    • 2013-10-05
    • 2014-10-25
    • 2017-08-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多