在 2025 收官前,看清 Data + AI 的真实走向,点击查看 BUILD 大会精华版 了解详情
写点什么

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:001244
用户头像

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

关注

评论

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

KubeVela 正式开源:一个高可扩展的云原生应用平台与核心引擎

阿里巴巴云原生

阿里云 开源 Kubernetes 云原生 OAM

OpenFeign和Consul爱恨交织的两天

编号94530

Spring Cloud Consul OpenFegin spring 5

粉丝求助:JAVA程序员,4年了,很迷茫,希望前辈可以给指出一个技术路线和需掌握的知识技能树;

Java架构师迁哥

深入浅出 Go - sync.Once 源码分析

helbing

Go 语言

Istio 1.8 发布——用户至上的选择

Jimmy Song

开源 云原生 Service Mesh istio

某美团程序员爆料:筛选简历时,用go语言的基本不看!网友:当韭菜还当出优越感了!

Java架构师迁哥

【云图说】第189期 初识数据仓库服务

华为云开发者联盟

数据库 数据仓库 数据

如何在 vuePress中添加博客导流公众号-即输入验证码解锁全站文章

itclanCoder

vuepress 解锁文章 博客引流 建站

一致性hash算法

天涯若海

架构师训练营第九周作业

我是谁

极客大学架构师训练营

《Python程序员面试算法宝典》PDF 超清版免费领取

计算机与AI

Python 面试 算法

面试题总结--HashMap、Volatile相关

彭阿三

涛涌天际,水利万物:黄浦江畔读懂城市智能体

脑极体

强化学习入门必看之强化学习导识

Alocasia

人工智能 学习

架构师Week5作业

lggl

作业

力扣(Leetcode)练习--给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序

Wynne

公众号高频被调整,它不是企业生产文章的机器

Linkflow

客户数据平台 CDP 私域流量

JVM入门,认识Class文件

Simon郎

JVM Java 分布式

深入理解h2和r2dbc-h2

程序那些事

响应式编程 R2DBC 程序那些事 响应式架构 r2dbc-h2

SQL数据库:GROUPING运算符

正向成长

GROUPING运算符

阿里作为内部参考的Redis文档现在开放下载,姐夫半夜不睡都在看

小Q

Java redis 学习 编程 面试

京东开发4年,想要跳槽去拼多多,落泪四4面,这年头跳槽可真难啊(还好不是裸辞)

Java~~~

架构 面试 编程语言 java架构师

甲方日常 54

句子

工作 随笔杂谈 日常

深入浅出 Go - sync.Map 源码分析

helbing

Go 语言

《迅雷链精品课》第五课:账户与账本

迅雷链

区块链

架构师Week5总结

lggl

总结

亚马逊全球百万钜惠引爆“黑五” 跨境狂欢“巅峰6日”震撼登场

爱极客侠

MySQL主从数据库没有同步怎么办?

冰河

MySQL 数据库 分布式 微服务

UNISKIN COO Kevin|营销数字化:数据沉淀和数据系统化运营一定要趁早!

Linkflow

营销数字化 客户数据平台 CDP

LAXCUS大数据集群操作系统挖矿

陈泽云

大数据 分布式计算 挖矿

苹果首发ARM架构电脑芯片,将对PC格局带来哪些影响?

脑极体

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