写点什么

Rubinius 内部细节:线程、对象空间和调试

  • 2007-08-06
  • 本文字数:2399 字

    阅读完需:约 8 分钟

作为 Evan Phoenix 谈 Rubinius:虚拟机内幕面面观的续篇,本文的第二部分将更加深入细节的实现。

Ruby 1.8.x 目前使用用户空间的线程,意味着它无法充分利用多核技术带来的便利,因为操作系统只能感知确定一个线程。Rubinius 目前使用用户空间线程,但是 Evan 同时也考虑了其它的解决方案:

对于 Rubinius 来说,在相同的地址空间中实现多重解释器是件价值不高的事情。编写整个虚拟机的时候就注意到了本地线程安全和可重入机制。这一切来自我在 Sydney 项目的工作经验,这个项目是对 Ruby 1.8.2 版的整理。 你可以轻松地创建两个 Machine(基础的数据结构)并同时将其初始化。它们随后都会保持完全的独立性。唯一要做的工作就是确保两个 Machine 可以合理地调度它们的代码。你甚至可以在两个不同的本地线程中启动两个虚拟机实例并且使其通过通道进行相互通信,这将给你带来一个真正可以利用多处理器特性的 Ruby。

在标准的 Ruby 发布版中,包含简单的调试器,它使用跟踪特性来实现。我们可以通过设置回调函数的方法来使用这个调试器,而这个回调函数在每行新的代码被执行之前被调用。回调函数通过set_trace_func方法来进行注册。(这种方式同样也用于概要分析)。这种方法带来的问题是它较高的系统开销。目前 Ruby 代码每执行一行,意味着要调用跟踪功能并且需要确定是否在这一点上延迟执行。另外也存在其它使用本体扩展的解决方案,就像 ruby-debug 或者 Visual Studio 2005 的 Ruby in Steel 插件所包含的 Cylon 调试器一样。

类似于 Ruby.NET、IronRuby 或是 XRuby 的 Ruby 编译器,恰好把调试信息生成在目标 IL 或字节码中,并且使用各自虚拟机的调试特性。

Rubinius 最近也获得了代码调试的支持,实现了断点的功能,它只有在某个断点命中的时候才会引入额外的性能开销。Evan 对于功能实现这样解释道:

我已经实现了基本的调试设施,这为我们带来了全速的断点(Full Speed Breakpoints)。全速断点意味着在运行期我们不会因为使用调试器而带来任何性能上的损耗了。我认为这是一次巨大的胜利,因为我听说 Ruby 调试器的速度一直以来是个瓶颈。我已经开始在 FSB 的基础之上建立更高级层次的功能,最终,我将可能将其写入类似于 ruby-debug 的机制当中,或是至少在感觉上是类似于 ruby-debug 的机制之中。 从技术角度说,FSB 通过字节码替换进行工作。当断点设置之后,系统使用反射机制查找精确的 CompiledMethod 对象,在这里需要设置调试断点。随后,系统计算字节码,判断断点需要在何处发生,并且使用叫做yield_debugger的奇妙的方式替代目前的指令。当命中这条指令时,系统将控制权交给调试器,这个调试器附属于当前运行的线程上。当这个方法需要继续执行时,旧的指令又被交换回来并且指令指针的值回退 1,并被重新激活。

这个功能运行得很好,因为调试器只是设置为空闲状态,等待运行实际代码的线程与之建立联系。这个功能运行得很好的另外一个原因是 Rubinius 中的方法上下文(Method Contexts)是被作为一等公民对待的。方法上下文和栈框架(Stack Frame)其实是一回事,它描述了方法运行的状态。在 Rubinius 中,你可以从虚拟机中获取系统中任何一个状态的方法上下文。随后就可以通过检查那个对象来发现此刻具体发生的事情。得到方法上下文最简单的方法就是调用“MethodContext.current”,将返回当前运行方法的方法上下文。

随着例如 JVM 和 CLR 这样 Ruby 托管运行时方式的实现,Ruby 的ObjectSpace特性已经出现了一点问题。ObjectSpace 允许对 Ruby 堆中所有可触及的对象进行访问,比方说:

ObjectSpace::each_object(Class){|x|<br></br> p x<br></br>}上述代码将打印当前 Ruby 软件栈中的所有 Class 对象。

JRuby 的开发者 Ola Bini 最近撰写了关于 ObjectSpace 在 JRuby 环境下的性能影响一文。因为 JVM(或者是 CLR)不允许直接访问堆,必须跟踪每一个对象的创建并且保留所有活动对象的清单。Evan 对于 rubinius 中 ObjectSpace 的情况,这样解释道:

我们实际上目前还没有实现它。我们将不会像 JRuby 实现那样有这么多的麻烦,因为我们具有对象存储单元的直接访问能力。Smalltalk语言实现这样的行为主要使用被称作next_object的单一 primitive 实现。当你在大多数对象中调用next_object时,将返回紧随它们之后的一个对象。目前,之后的定义是与具体实现相关的,但是通常的定义是在内存中当前对象的下一个对象。

需要注意到这种方式在任何情况下都不可能保证是无损或者精确的,并且,为了以一种不困扰使用这套接口的开发人员的方式完成,这种方式仍然会有一些性能上的额外开支。这可能令人困惑,因为虚拟机(包括 Rubinius)依赖于这样一个事实:它们可以在内存中重新整理对象,而不会使已经运行的代码产生任何问题。

实际上 Rubinius 这样做是很频繁的,因为垃圾回收器中有一种就是一个复制 - 压缩收集器,因此较新的对象要经常移动。如果你在一个对象上调用next_object,方法返回对象 B,你绝不能指望在下次调用next_object的时候,返回的还是对象 B。

与之相关的一件事是 Rubinius 目前已经完成了将对象的object_id和它在内存中的位置解耦。MRI 的object_id返回对象在内存中的地址,这样之后,我们就可以通过调用ObjectSpace._id2ref,以object_id返回的数值作为参数,从而返回相应对象。在 MRI 中,_id2ref要实现起来相当容易,因为它只需直接从内存的相应位置取得对象就可以了。但是经常用到_id2ref的人知道,有时候你取出来的对象和原先放入的完全不同。这是因为旧的对象已经完成生命周期,而新的对象已经被分配在同一个地方。这虽然没有妨碍人们继续使用_id2ref,但人们应当清楚,_id2ref并不是像人们想象的那样工作的。

无论如何,Rubinius 目前还不支持_id2ref,因为object_id保存的并不是内存地址。我们会找到支持的方式的,但和 JRuby 一样,这可能会成为一个彻底的额外性能开支。

查看英文原文: Evan Phoenix on Rubinius - VM Internals Interview

2007-08-06 03:001199
用户头像

发布了 74 篇内容, 共 15.1 次阅读, 收获喜欢 3 次。

关注

评论

发布
暂无评论
发现更多内容

从GPT模型来看ChatGPT 如何在语言处理领域引领潮流?

霍格沃兹测试开发学社

Vision Pro 开发实践(一)

京东零售技术

人工智能 企业号 4 月 PK 榜 Vision pro

透视微盟2023:SaaS基本盘稳健背后,大客化成色几何?

ToB行业头条

GPT 模型解析:ChatGPT 如何在语言处理领域引领潮流?

测试人

软件测试

云行|中国速度,智算未来!天翼云为宁波数智化发展注入新动能!

天翼云开发者社区

人工智能 云计算 云服务

智能商品管理:品牌实现以销定产的新引擎

第七在线

深度解密京东中台底层支撑框架

京东零售技术

Java 后端 企业号 4 月 PK 榜

京东云开源软件治理工具SSCM强势来袭!免费试用

京东科技开发者

京东为openKylin新增SBOM利器,保障软件供应链安全和可追溯性

京东科技开发者

2024第104届上海电子展会

AIOTE智博会

电子展 电子信息展 上海电子展

新增功能:节点支持only配置啦!!!

Jianmu

BOE(京东方)发布2023年年报 稳定经营促进高质量发展

爱极客侠

正则表达式-子表达式&回溯引用

追随月光的战士

#正则表达式

玩转云端| 网络也i人?咋变显眼包?天翼云AccessOne边缘接入来了!

天翼云开发者社区

云计算 网络安全 云服务 边缘安全

NeuronEX 3.2.0 发布:增强数据采集、分析计算和管理功能

EMQ映云科技

一文教你实战构建消息通知系统Django

华为云开发者联盟

Python 华为云 华为云开发者联盟 企业号2024年4月PK榜 Web应用程序

利用AI大模型实现自然语言到SQL的转换及其优化

百度开发者中心

自然语言处理 AI大模型

产品待办列表梳理步骤

ShineScrum

大模型LLM在Text2SQL上的应用实践

百度开发者中心

自然语言处理 大模型 LLM

升级ChatGPT4.0,原来还需要注意这些?

蓉蓉

openai #人工智能 ChatGPT GPT-4

通义灵码走进武汉大学:让 AI 编码助手激活大学生的创造力

阿里巴巴云原生

阿里云 AI 云原生

Rubinius内部细节:线程、对象空间和调试_Ruby_Werner Schuster_InfoQ精选文章