AICon 上海站|日程100%上线,解锁Al未来! 了解详情
写点什么

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

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

关注

评论

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

从项目经理过渡到ScrumMaster的三个技巧

ShineScrum

Scrum 敏捷 ScrumMaster 项目经理

DevSecOps 安全即代码基础指南

SEAL安全

DevOps DevSecOps 云安全 DevSecOps和敏捷

华为伙伴暨开发者大会之GaussDB高校行,产教融合持续赋能人才培养

科技怪咖

【小程序项目开发-- 京东商城】uni-app之自定义搜索组件(中)-- 搜索建议

计算机魔术师

8月月更

【高并发】亿级流量场景下如何为HTTP接口限流?看完我懂了!!

冰河

并发编程 多线程 高并发 协程 异步编程

科普达人丨一图看懂安全组

阿里云弹性计算

防火墙 安全组 ECS

干货 | Dubbo 接口测试原理及多种方法实践总结

霍格沃兹测试开发学社

拔掉电源会怎样?GaussDB(for Redis)双活让你有备无患

科技怪咖

用GaussDB(for Redis)存画像,推荐业务轻松降本60%

科技怪咖

日拱算法:什么是“煎饼排序”?

掘金安东尼

算法 8月月更

GaussDB(for Cassandra)新特性发布:Lucene二级索引,让复杂查询更智能

科技怪咖

列存数据仓库怎样做才能更高效

jiangxl

Flomesh 发布 Pipy 0.50.0

Flomesh

BaaS助力区块链技术开启下一轮应用热潮

旺链科技

区块链 产业区块链 企业号九月金秋榜 BaaS平台

实战 | 电商业务的性能测试(一): 必备基础知识

霍格沃兹测试开发学社

3. Docker镜像详解

鱼书

c++ 8月月更

【小程序项目开发--京东商城】uni-app之自定义搜索组件(上)-- 组件UI

计算机魔术师

8月月更

STM32+M5311对接 OneNET 项目记录

矜辰所致

stm32 8月月更 M5311

【Django | allauth】登录_注册_邮箱验证_密码邮箱重置

计算机魔术师

8月月更

HMS Core基于地理位置请求广告,流量变现快人一步

HarmonyOS SDK

广告sdk

科普达人丨一图看懂镜像

阿里云弹性计算

镜像 ECS

电商出海,独立站小程序化能更好的连接人和应用

Speedoooo

小程序 移动开发 DTC 独立站 移动框架

2. 字符串、向量和数组

鱼书

c++ 操作系统 8月月更

运维理想和现实,你是?

嘉为蓝鲸

运维 AIOPS 故障处理 补丁

快速开始安装部署ArkID构建IDaaS,轻松拥有标准化用户身份体系

龙归科技

单点登录 Idaas ArkID

基于 JMeter 完成 Dubbo 接口的测试

霍格沃兹测试开发学社

京东云PostgreSQL在GIS场景的应用分享

京东科技开发者

postgresql 检索 jieba 京东云 信息检索

阿里云高庆瑞:如何快速搭建低成本高弹性的云上应用

阿里云弹性计算

应用 自动化运维 CloudOps 弹性伸缩

前后端数据接口协作提效实践

百度Geek说

Java 数据 企业号九月金秋榜

开源一夏 | count(列名)、 count(常量)、 count(*)区别

六月的雨在InfoQ

开源 count MySQL 数据库 MySQL InnoDB 8月月更

给ShardingSphere提了个PR

艾小仙

Java GitHub 程序员 shardingsp

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