QCon 演讲火热征集中,快来分享技术实践与洞见! 了解详情
写点什么

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

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

关注

评论

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

Volcano社区正式发布v1.2.0版本

华为云原生团队

人工智能 开源 AI 云原生

用 Go 写一个轻量级的 ssh 批量操作工具

冯骐

运维 SSH Go 语言

思科设备漏洞 CVE-2018-0171 的快速修复

冯骐

Python 运维 安全 网络 交换机

《精通比特币》学习笔记(第一章)

棉花糖

区块链 读书笔记

【LeetCode】俄罗斯套娃信封问题Java题解

Albert

算法 LeetCode 28天写作

交换机配置的自动备份与变更告警

冯骐

Python 运维 网络 备份 multissh

Pgbouncer最佳实践:系列四

PostgreSQLChina

数据库 postgresql 软件 开源社区

在主动要求涨工资这事上,不要学我!从第一份工资800开始说起

四猿外

程序员 涨薪 工资 收入 跳槽

元素XPATH定位点击为什么老失败?

小小娃爱吃甜食

Python 自动化 测试 selenium

上云三问 | 京东云这次的大促有哪些“不一样”?

京东科技开发者

云主机 云安全

LeetCode题解:190. 颠倒二进制位,移动n,JavaScript,详细注释

Lee Chen

算法 大前端 LeetCode

Golang 中的并发限制与超时控制

冯骐

并发 超时 Go 语言

用 Go 写一个轻量级的 ldap 测试工具

冯骐

运维 开发 ldap Go 语言

面试官:如何用SpringCloud从零设计一个大型电商平台?

Java架构追梦

Java 架构 面试 微服务 SpringCloud

上过云么?一行代码秒上云体验过么?

华为云开发者联盟

华为云 云服务器 devcloud 代码托管

区块链赋能电子合同!构建互联网可信生态整体解决方案

源中瑞-龙先生

从小厂逆袭快手,我是如何准备面试的

haxianhe

failure: repodata/repomd.xml from cloudera-manager: [Errno 256] No more mirrors to try

wjchenge

量化交易马特机器人(币掌柜/小树/小喜/瓦力)系统开发策略搭建

薇電13242772558

数字货币 量化策略

如何使用Excel表格精准分析PT100温度阻值关系?

不脱发的程序猿

28天写作 PT100 温度采集算法 Excel数据分析 传感器采集

Nginx安装后要做的第一件事

运维研习社

nginx WEB安全

Serverless 极致弹性解构在线游戏行业痛点

阿里巴巴云原生

Serverless 微服务 开发者 云原生 消息中间件

3 月日更挑战|春日胜景,记录暖心每一天

InfoQ写作社区官方

七日更 3月日更 热门活动

Spring Boot+Maven实现车牌训练、识别系统

不脱发的程序猿

maven Spring Boot 图像处理 28天写作 车牌训练、识别系统

迄今为止最好用的Flink SQL教程:Flink SQL Cookbook on Zeppelin

Apache Flink

flink

简述Electron的发展和应用

anyRTC开发者

WebRTC 跨平台 Electron RTC sdk

Open-Falcon 中的 LDAP 认证

冯骐

Python 运维 Open-Falcon ldap Go 语言

用 Nginx 的 auth_request 模块集成 LDAP 认证

冯骐

Python nginx 运维 ldap 认证

智汇华云 | 深入理解Istio中的证书管理

华云数据

【经验分享】如何释放企业CMDB价值?——CMDB建设路径浅谈

嘉为蓝鲸

运维自动化 配置中心 CMDB 配置信息 配置管理

拍乐云加盟UCloud优云精选计划,构建云上的美好互动

拍乐云Pano

ucloud 云服务 RTC 拍乐云 白板

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