我正在开发一个相当大的ruby(非rails)应用程序。考虑到它的庞大和复杂性,它的速度相当快(使用Ruby!),但有时我会用手指敲一个方法名,得到NoMethodError。
通常当这种情况发生时,应用程序会挂起20到30秒,以便打印出回溯。
具体地说,如果我做这样的事情:
puts "about to crash!"
Array.new().inspekt # NoMethodError here
我看到了“即将坠毁!“马上,然后20秒左右似乎什么都没有发生,然后我终于得到了NoMethodError和回溯。
起初我以为这可能是“did you mean”gem,所以我在命令行上使用--disable-did_you_mean关闭了它,这样就关闭了“did you mean”建议,但没有什么能加快回溯速度。
有趣的是,这只适用于NoMethodError。
如果我导致其他异常,例如:
puts "about to crash!"
a = 3/0
然后我立即看到了回溯。
更奇怪的是,如果我在“即将崩溃”之后中断这个过程!”(例如在unix上使用ctrl-c),然后我立即得到NoMethodError,并且它是回溯。所以它有信息--但是ruby可能被困在试图清理一些东西上,一些只能在NoMethodError上清理的东西?
信息:ruby 2.7.0
操作系统:CentOS Linux 7.5.1804
更新-到目前为止的答复:每个人似乎都在关注Ruby代码的回溯和分析。
但减速并没有发生在那里。在减速期间没有执行Ruby代码行。在此之前的所有行,“在回溯中”已经被执行,并且在大约一秒左右的时间内。然后系统在puts和NoMethodError之间挂起。这中间没有ruby代码需要分析,所以任何分析器查看用ruby脚本编写的代码都不会有帮助。减速是ruby内部的事情,不在我的代码中,除非我对发生的事情感到非常困惑。
说清楚
Line 10042: puts "HERE" # Happens at ~1s
Line 10043: Array.new().inspekt # Happens at ~20-30s
这些行之间没有要分析的代码。在第10042行执行之前,20- 30 s不会在任何代码中发生,因此分析不会有帮助。
我 * 确实 * 有其他光纤被暂停。但是这里没有任何代码会屈服于它们。有没有可能存在一些奇怪的内置yield代码,当遇到异常时,这些代码会尝试运行其他(暂停的)纤程?我想不出你想要这种行为的原因,也想不出为什么它会是灾难性的,但是我想不出还有什么会导致这个问题的(这也是可以用ctrl-c杀死的!))
2条答案
按热度按时间gtlvzcf81#
我会尝试在那里调试完整的回溯,看看实际发生了什么
在我的例子中,我用ruby 2.6.3和irb得到了20行回溯,如果这没有告诉你任何有趣的事情,我会做一些繁琐的工作,通过修改回溯的每个文件来测量每个运行时,并打印每一步的时间,调试耶!
jv4diomz2#
我终于弄明白了问题所在。
问题是NoMethodError将使用inspect,这将检查出NoMethodError的接收方的所有示例变量。
由于我正在编写一个相当大的程序,其中包含大量数据的大量对象,因此我的许多顶级对象都需要花费大量时间来计算“检查”字符串。
有趣的是,如果你'ctrl-c' NoMethodError,它会停止尝试构建inspect字符串,而只是使用类名。我有一种感觉,其他示例在试图构建inspect字符串时内存不足,最终会出现在同一个地方,打印一个简单的NoMethodError,而不知道幕后发生了什么(和失败)。
解决方案非常简单,我可以定义“inspect”,以便在所有对象上更合理。另一种选择是在NoMethodError中重新定义'detailed_message',以便在计算消息之前临时将错误接收方的'inspect'方法重新定义为'to_s'。这可能适用于具有相同类型问题的其他异常,但我目前只在NoMethodError上看到它。
代码如下-考虑到我对singleton_classes的理解(缺乏),我可能在范围内做了一些不太正确的事情,但它似乎在各种测试中正常工作。请注意,只有在接收方显式定义了to_s时,它才能解决这个问题--这是该方法第4行中的一个检查。这将错过一些情况,例如,您继承了一个类,但没有重新定义为_s,因此您可能希望在代码中删除它。YMMV