微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

有人可以帮我理解下面的Ruby片段吗?

我最近在Tomcat的J Ruby上遇到了运行Sinatra的permgen内存泄漏.问题与Sinatra用来支持各种模板选项的Tilt库有关.旧代码(此处未包含)正在生成内存泄漏.新代码(下面)没有,事实上我发现permgen GC现在正在运行.

Ruby应该是自我描述的,但我无法通过阅读它来弄清楚这些代码.有嵌套的类逃逸.为什么?为什么定义一个方法然后解除绑定?

为什么代码编译了一堆模板并保留它们以便重复使用如此复杂的外观?

另外:如果有任何GitHub员工正在查看此问题,您能否向GitHub添加一些功能,允许用户代码片段中插入问题?

(此代码https://github.com/rtomayko/tilt/blob/master/lib/tilt.rb解除)

def compile_template_method(locals)  
  source,offset = precompiled(locals)  
  offset += 5  
  method_name = "__tilt_#{Thread.current.object_id.abs}"  
  Object.class_eval <<-RUBY,eval_file,line - offset  
    #{extract_magic_comment source}  
    TOPOBJECT.class_eval do  
      def #{method_name}(locals)    
        Thread.current[:tilt_vars] = [self,locals]  
        class << self  
          this,locals = Thread.current[:tilt_vars]  
          this.instance_eval do  
            #{source}  
          end  
        end  
      end  
    end  
  RUBY  
  unbind_compiled_method(method_name)  
end

解决方法

There are nested class evals. Why?

这种方法看起来像是来自战争伤痕累累,修复和修补的生产代码,而不是优雅的自描述代码(所以也许我们可以原谅他们一点).

那为什么两个人逃避?在第二个嵌套的“真实”模板方法代码可以被评估之前,要评估的代码必须以正确的源编码作为前缀,该编码可能已被定义为模板文件中的“魔术注释”.

一旦正确设置了字符串编码,就可以尝试真正的class_eval.另一种说法可能是“这是编写源代码的源代码的源代码”!

据推测,这是为了修复Ruby 1.9中可能出现的兼容性问题,其中正在编译的模板可能包含与Tilt库源代码本身(US-ASCII编码)的编码不同的字符编码(UTF-8),会导致模板字符串的错误评估(因为字符串编码已经在调用模板文件的主机代码中设置).

Why is a method being defined and then unbound?

澄清一下:在Ruby中,unbound与undefined不同.

未绑定方法作为可以调用的UnboundMethod类型的自由方法对象存在,尽管它们不再与特定对象关联.未绑定的方法不再具有接收器.

为了创建一个未绑定的方法,它首先被绑定(定义为)一个对象.这就是为什么编译模板方法可以从顶级对象中快速删除的原因,因为它只是生成未绑定方法所必需的临时安排.

此技术用于使用可以使用特定于给定类的不同实例的编译模板,而无需以任何可见或永久的方式更改根对象或第三方开发人员的客户端类.

通过将编译的模板方法与特定的客户端代码对象解除关联,在以后调用使用该类型的对象的模板期间,编译的模板方法可以稍后反弹到该对象的类的新实例.

例如,给定以下ERB模板:

<p>Hello <%= @name %></p>

……以及以下调用代码

scott = Person.new
scott.name = "Scott"
output = template.render(scott)
=> "<p>Hello Scott</p>"

在第一次渲染期间,模板被评估并针对TOPOBJECT对象的实例进行编译.编译的模板方法将命名为“__tilt_2151955260”.然后,此方法将被解除绑定,以便再次对TOPOBJECT类型的所有实例(根据Ruby版本只是Object或BasicObject)使用,因此可以用于任何客户端对象类型.

下次渲染模板时,编译的模板方法与TOPOBJECT的’baq’实例绑定:

baq = Person.new
baq.name = "Baq"
output = template.render(baq)

在引擎盖下,当调用template.render(baq)时,未绑定的编译模板方法将绑定到Person的’baq’实例:

__tilt_2151955260.bind(baq).call

不必每次调用class_eval都会带来可观的性能提升.

Why is code that compiles a bunch of templates and keeps them around for
re-use so complicated looking?

我的评估是,尽管代码实现确实看起来确实看起来不那么复杂,但是这些间接层通常在框架代码中是必需的,这些代码旨在使公共API非常简单和甜蜜地为成千上万的其他开发人员消费,即使它是以少数必须维护它的开发人员为代价的.

由于在许多不同的语言环境中消耗的API引起的实际问题以及来自世界各地的许多编码,代码复杂性(双eval嵌套)也增加了.

脚注:
此后,问题中引用的Template类已重构为单独的文件github.com/rtomayko/tilt/blob/master/lib/tilt/template.rb

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐