<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>zhuoerri (秦阳)</title>
    <link>https://ruby-china.org/zhuoerri</link>
    <description></description>
    <language>en-us</language>
    <item>
      <title>Object Shapes 浅析</title>
      <description>&lt;h2 id="前言"&gt;前言&lt;/h2&gt;
&lt;p&gt;本人水平有限，如有错误，欢迎指正与补充。&lt;/p&gt;

&lt;p&gt;ruby 3.2 除了 YJIT，还有一些别的小优化，例如也是来自 shopify 的 Object Shapes。其作用有提高缓存命中率，加速实例变量 (和别的属性) 的读写。&lt;/p&gt;
&lt;h2 id="Shape Tree"&gt;Shape Tree&lt;/h2&gt;
&lt;p&gt;Object Shapes 的思路是设计一个全局的前缀树 Shape Tree, 每新增一个实例变量时，就增加一个节点。如下 foo 的形状 id 是 2, bar 的形状 id 是 4&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Foo&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;
    &lt;span class="c1"&gt;# Currently this instance is the root shape (ID 0)&lt;/span&gt;
    &lt;span class="vi"&gt;@a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="c1"&gt;# Transitions to a new shape via edge @a (ID 1)&lt;/span&gt;
    &lt;span class="vi"&gt;@b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="c1"&gt;# Transitions to a new shape via edge @b (ID 2)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Bar&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;
    &lt;span class="c1"&gt;# Currently this instance is the root shape (ID 0)&lt;/span&gt;
    &lt;span class="vi"&gt;@a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="c1"&gt;# Transitions to shape defined earlier via edge @a (ID 1)&lt;/span&gt;
    &lt;span class="vi"&gt;@c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="c1"&gt;# Transitions to a new shape via edge @c (ID 3)&lt;/span&gt;
    &lt;span class="vi"&gt;@b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="c1"&gt;# Transitions to a new shape via edge @b (ID 4)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;foo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Foo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="c1"&gt;# blue in the diagram&lt;/span&gt;
&lt;span class="n"&gt;bar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Bar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="c1"&gt;# red in the diagram&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/zhuoerri/b6782ed3-13c5-4f19-ab8e-91acbeaf8439.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;这样的好处是，例如根据这棵树，就可以知道 foo 的&lt;code&gt;@b&lt;/code&gt;实例变量是在第二的位置，直接读取 foo 的第二个实例变量就能读取到&lt;code&gt;@b&lt;/code&gt;，而不用顺着类的继承链找。&lt;/p&gt;
&lt;h2 id="Inline Cache"&gt;Inline Cache&lt;/h2&gt;
&lt;p&gt;这棵树的另一个好处是，可以提高缓存命中率。ruby 写入实例变量的字节码 setinstancevariable，有一个内联缓存 inline cache, 例如下图的&lt;code&gt;&amp;lt;is:0&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;is:1&amp;gt;&lt;/code&gt;&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;a&lt;/span&gt;
  &lt;span class="vi"&gt;@a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="vi"&gt;@b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="no"&gt;RubyVM&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;InstructionSequence&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:a&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;disasm&lt;/span&gt;

&lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="ss"&gt;disasm: &lt;/span&gt;&lt;span class="c1"&gt;#&amp;lt;ISeq:a@(irb):17 (17,0)-(20,3)&amp;gt; (catch: FALSE)&lt;/span&gt;
&lt;span class="mo"&gt;0000&lt;/span&gt; &lt;span class="n"&gt;putobject_INT2FIX_0_&lt;/span&gt;                                             &lt;span class="p"&gt;(&lt;/span&gt;  &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="no"&gt;LiCa&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="mo"&gt;0001&lt;/span&gt; &lt;span class="n"&gt;setinstancevariable&lt;/span&gt;                    &lt;span class="ss"&gt;:@a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;is&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="mo"&gt;0004&lt;/span&gt; &lt;span class="n"&gt;putobject_INT2FIX_0_&lt;/span&gt;                                             &lt;span class="p"&gt;(&lt;/span&gt;  &lt;span class="mi"&gt;19&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="no"&gt;Li&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="mo"&gt;0005&lt;/span&gt; &lt;span class="nb"&gt;dup&lt;/span&gt;
&lt;span class="mo"&gt;0006&lt;/span&gt; &lt;span class="n"&gt;setinstancevariable&lt;/span&gt;                    &lt;span class="ss"&gt;:@b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;is&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="mo"&gt;000&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt; &lt;span class="n"&gt;leave&lt;/span&gt;                                                            &lt;span class="p"&gt;(&lt;/span&gt;  &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="no"&gt;Re&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ruby 3.2 前的实例变量的缓存逻辑是根据类，如果子类继承的话，会让缓存无效。但现在根据前缀树 Shape Tree, 则下面的 hoge 和 fuga 是相同的形状 id, initialize 方法里能让缓存命中&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Hoge&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;
    &lt;span class="c1"&gt;# fuga初始状态，形状id为0&lt;/span&gt;
    &lt;span class="vi"&gt;@a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="c1"&gt;# &amp;lt;is:0&amp;gt;缓存里，记录了上次hoge的{ 形状id为0 =&amp;gt; 位置1 }, 命中缓存，从而得知@a在第一个的位置, 然后fuga形状id变为1&lt;/span&gt;
    &lt;span class="vi"&gt;@b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="c1"&gt;# &amp;lt;is:1&amp;gt;缓存里，记录了上次hoge的{ 形状id为1 =&amp;gt; 位置2 }, 命中缓存，从而得知@b在第二个的位置，然后fuga形状id变为2&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Fuga&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Hoge&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;hoge&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Hoge&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;span class="n"&gt;fuga&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Fuga&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="红黑树"&gt;红黑树&lt;/h2&gt;
&lt;p&gt;为了防止实例变量太多，前缀树太深，不好查找。2023 年，tenderlove 加入了红黑树，当 Shape Tree 深度达到阈值后，新建一颗对应的红黑树来加速查找&lt;/p&gt;

&lt;p&gt;简略后源码如下：&lt;/p&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* https://github.com/ruby/ruby/blob/5124f9ac7513eb590c37717337c430cb93caa151/vm_insnhelper.c#L1616C1-L1641C2 */&lt;/span&gt;

&lt;span class="cm"&gt;/* 设置实例变量的字节码对应的函数 */&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kr"&gt;inline&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
&lt;span class="nf"&gt;vm_setinstancevariable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;rb_iseq_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;iseq&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ID&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IVC&lt;/span&gt; &lt;span class="n"&gt;ic&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;shape_id_t&lt;/span&gt; &lt;span class="n"&gt;dest_shape_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;attr_index_t&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;vm_ic_atomic_shape_and_index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;dest_shape_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="cm"&gt;/* 从inline cache缓存中读取形状id和实例变量位置index */&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;UNLIKELY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;UNDEF_P&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vm_setivar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dest_shape_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* vm_setivar会检查缓存的dest_shape_id是否匹配当前对象obj的形状id */&lt;/span&gt;
        &lt;span class="n"&gt;vm_setivar_slowpath_ivar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;iseq&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ic&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="cm"&gt;/* 如果inline cache缓存没命中，则还是顺着继承链找实例变量位置, 把位置写入新Shape Tree节点。同时写入inline cache缓存，供下次尝试匹配 */&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="cm"&gt;/* github.com/ruby/ruby/blob/5124f9ac7513eb590c37717337c430cb93caa151/shape.c#L459C1-L487C2 */&lt;/span&gt;

&lt;span class="cm"&gt;/* Shape Tree 创建子节点的函数 */&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;rb_shape_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="nf"&gt;rb_shape_alloc_new_child&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rb_shape_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;shape_type&lt;/span&gt; &lt;span class="n"&gt;shape_type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;rb_shape_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;new_shape&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rb_shape_alloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;shape_type&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="cm"&gt;/* 新建节点 */&lt;/span&gt;

    &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;shape_type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;SHAPE_IVAR&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="cm"&gt;/* 如果是用于实例变量的节点 */&lt;/span&gt;
        &lt;span class="n"&gt;new_shape&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;next_iv_index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;next_iv_index&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="cm"&gt;/* 新节点的实例变量位置是父节点位置 + 1 */&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_shape&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;next_iv_index&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ANCESTOR_CACHE_THRESHOLD&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;redblack_cache_ancestors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_shape&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="cm"&gt;/* 如果深度超过阈值，新建一个红黑树来加速查找 */&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;SHAPE_FROZEN&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;SHAPE_T_OBJECT&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;new_shape&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;next_iv_index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;next_iv_index&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;SHAPE_OBJ_TOO_COMPLEX&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;SHAPE_ROOT&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;rb_bug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Unreachable"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;new_shape&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="参考资料"&gt;参考资料&lt;/h2&gt;
&lt;p&gt;本文只简单讲了缓存命中率，Object Shapes 还有精简代码，有利 jit 等好处，详情见下面的帖子和视频演讲&lt;/p&gt;

&lt;p&gt;&lt;a href="https://bugs.ruby-lang.org/issues/18776" rel="nofollow" target="_blank"&gt;https://bugs.ruby-lang.org/issues/18776&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://rubykaigi.org/2022/presentations/jemmaissroff.html" rel="nofollow" target="_blank"&gt;https://rubykaigi.org/2022/presentations/jemmaissroff.html&lt;/a&gt;&lt;/p&gt;</description>
      <author>zhuoerri</author>
      <pubDate>Sat, 03 Feb 2024 22:27:53 +0800</pubDate>
      <link>https://ruby-china.org/topics/43580</link>
      <guid>https://ruby-china.org/topics/43580</guid>
    </item>
    <item>
      <title>垃圾回收原理浅析</title>
      <description>&lt;h2 id="前言"&gt;前言&lt;/h2&gt;
&lt;p&gt;本人水平有限，如有错误，欢迎指正和补充。&lt;/p&gt;

&lt;p&gt;本文会用自问自答的形式，用【公司裁员】打比方，结合 Ruby 3.1 源码，科普 Ruby 垃圾回收的基本原理。内容包含：Ruby 内存模型，标记回收算法，分代回收，增量回收，碎片整理，最后借助最新的  Variable Width Allocation，列举一下 局部性原理。&lt;/p&gt;
&lt;h2 id="Ruby内存模型"&gt;Ruby 内存模型&lt;/h2&gt;&lt;h2 id="Q: Ruby对象在内存里是怎么存储管理的？"&gt;Q: Ruby 对象在内存里是怎么存储管理的？&lt;/h2&gt;
&lt;p&gt;每个 Ruby 对象占用一个&lt;code&gt;slot&lt;/code&gt;，一个&lt;code&gt;slot&lt;/code&gt; 在 64 位的机器上占 40 字节，多个&lt;code&gt;slot&lt;/code&gt;聚成一个 Ruby&lt;code&gt;page&lt;/code&gt;。所有&lt;code&gt;page&lt;/code&gt;组成了&lt;code&gt;Ruby HEAP&lt;/code&gt;。如果 Ruby 对象太大，例如一个很长的 String，40 个字节放不下。则在总&lt;code&gt;HEAP&lt;/code&gt;里 malloc 一块额外内存来存储。如下图，第一个&lt;code&gt;slot&lt;/code&gt;指向了额外数据。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌──────────────────────────────────────────────────────────────────────────────────┐
│                                                                                  │
│   HEAP  ┌────────────────────────────────────────────────────────────────────┐   │
│         │    RUBY HEAP                                                       │   │
│         │ ┌───────────────────┐ ┌───────────────────┐ ┌───────────────────┐  │   │
│         │ │ page              │ │ page              │ │ page              │  │   │
│         │ │                   │ │                   │ │                   │  │   │
│         │ │ ┌─────┐   ┌─────┐ │ │ ┌─────┐   ┌─────┐ │ │ ┌─────┐   ┌─────┐ │  │   │
│         │ │ │slot │   │slot │ │ │ │slot │   │slot │ │ │ │slot │   │slot │ │  │   │
│      ┌──┼─┼─┤     │   │     │ │ │ │     │   │     │ │ │ │     │   │     │ │  │   │
│      │  │ │ └─────┘   └─────┘ │ │ └─────┘   └─────┘ │ │ └─────┘   └─────┘ │  │   │
│      │  │ │                   │ │                   │ │                   │  │   │
│      │  │ │ ┌─────┐   ┌─────┐ │ │ ┌─────┐   ┌─────┐ │ │ ┌─────┐   ┌─────┐ │  │   │
│      │  │ │ │slot │   │slot │ │ │ │slot │   │slot │ │ │ │slot │   │slot │ │  │   │
│      │  │ │ │     │   │     │ │ │ │     │   │     │ │ │ │     │   │     │ │  │   │
│      │  │ └─┴─────┴───┴─────┴─┘ └─┴─────┴───┴─────┴─┘ └─┴─────┴───┴─────┴─┘  │   │
│      │  │                                                                    │   │
│      │  └────────────────────────────────────────────────────────────────────┘   │
│      │                                                                           │
│      │                                                                           │
│      │                                                                           │
│     ┌▼───────────────┐                                                           │
│     │ extra data     │                                                           │
│     │                │                                                           │
│     └────────────────┘                                                           │
│                                                                                  │
│                                                                                  │
└──────────────────────────────────────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;heap 和 page 的源码在 &lt;a href="https://github.com/ruby/ruby/blob/b43ad6b802b805cec6bee503e352d9697da8ca56/gc.c#L665" rel="nofollow" target="_blank" title=""&gt;gc.c&lt;/a&gt; , 精简后如下&lt;/p&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;typedef&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;rb_heap_struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;heap_page&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;free_pages&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;list_head&lt;/span&gt; &lt;span class="n"&gt;pages&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="cm"&gt;/* 存放的所有pages， 指向了第一个page */&lt;/span&gt;
    &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;heap_page&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;sweeping_page&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="cm"&gt;/* 用来迭代每个pages的临时指针 */&lt;/span&gt;
    &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;
    &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;total_pages&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;      &lt;span class="cm"&gt;/* 总page数 */&lt;/span&gt;
    &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;total_slots&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;      &lt;span class="cm"&gt;/* 总slot数 */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;rb_heap_t&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;heap_page&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;short&lt;/span&gt; &lt;span class="n"&gt;total_slots&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="cm"&gt;/* page内总slot数 */&lt;/span&gt;
    &lt;span class="kt"&gt;short&lt;/span&gt; &lt;span class="n"&gt;free_slots&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="cm"&gt;/* page内空闲的slot数 */&lt;/span&gt;
    &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;
    &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;heap_page&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;free_next&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="cm"&gt;/* 下一个可用的page */&lt;/span&gt;
    &lt;span class="n"&gt;RVALUE&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="cm"&gt;/* page内第一个slot地址 */&lt;/span&gt;
    &lt;span class="n"&gt;RVALUE&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;freelist&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="cm"&gt;/* page内空闲的slot列表 */&lt;/span&gt;
    &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="Q: 还是不懂，能打个比方吗?"&gt;Q: 还是不懂，能打个比方吗？&lt;/h2&gt;
&lt;p&gt;可以用【公司裁员】来比喻 Ruby 垃圾回收。&lt;code&gt;page&lt;/code&gt;可以看作公司【办公室】,&lt;code&gt;slot&lt;/code&gt; 看作【工位】, &lt;code&gt;Ruby对象&lt;/code&gt;看作【员工】，每个【员工】占一个【工位】。如果一个【工位】不够放私人物品，则在外部【储物柜】(&lt;code&gt;总HEAP&lt;/code&gt;) 存放额外数据&lt;/p&gt;
&lt;h2 id="Q: 如果【工位】(slot)不够了？"&gt;Q: 如果【工位】(&lt;code&gt;slot&lt;/code&gt;) 不够了？&lt;/h2&gt;
&lt;p&gt;裁掉对公司无用的【员工】(&lt;code&gt;Ruby对象&lt;/code&gt;), 空出【工位】(&lt;code&gt;slot&lt;/code&gt;) 给【新员工】(&lt;code&gt;新Ruby对象&lt;/code&gt;)&lt;/p&gt;
&lt;h2 id="Q: 如果裁员后【工位】(slot)还是不够?"&gt;Q: 如果裁员后【工位】(&lt;code&gt;slot&lt;/code&gt;) 还是不够？&lt;/h2&gt;
&lt;p&gt;租更多的【办公室】(向操作系统要更多的 Ruby&lt;code&gt;page&lt;/code&gt;)&lt;/p&gt;
&lt;h2 id="Q: 什么时候退租【办公室】(page),还内存给操作系统？"&gt;Q: 什么时候退租【办公室】(&lt;code&gt;page&lt;/code&gt;),还内存给操作系统？&lt;/h2&gt;
&lt;p&gt;空闲【工位】(&lt;code&gt;slot&lt;/code&gt;) 的空置率最多 65%。如果裁员完空余【工位】(&lt;code&gt;slot&lt;/code&gt;) 超过 65% 的，则超出 65% 的部分是可以退租的额度。Ruby 会在退租额度内，选择完全空置的【办公室】(&lt;code&gt;page&lt;/code&gt;) 退租。Ruby 管这种完全空置的&lt;code&gt;page&lt;/code&gt;叫&lt;code&gt;tomb page&lt;/code&gt;, 管还有 Ruby 对象的&lt;code&gt;page&lt;/code&gt;叫&lt;code&gt;eden page&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;是否还内存给操作系统的源码也在 &lt;a href="https://github.com/ruby/ruby/blob/b43ad6b802b805cec6bee503e352d9697da8ca56/gc.c#L8018" rel="nofollow" target="_blank" title=""&gt;gc.c&lt;/a&gt;, 精简后如下&lt;/p&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* 在确定了裁员名单后，正式裁员前。会调这个gc_marks_finish函数 */&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;
&lt;span class="nf"&gt;gc_marks_finish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rb_objspace_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;objspace&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;

  &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;total_slots&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;heap_allocatable_pages&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;HEAP_PAGE_OBJ_LIMIT&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;heap&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;total_slots&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;sweep_slots&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;total_slots&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;objspace&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;marked_slots&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="cm"&gt;/* 总slot数减去保留的slot数，就是当前空闲的slot数 */&lt;/span&gt;
  &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;max_free_slots&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;total_slots&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;gc_params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;heap_free_slots_max_ratio&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="cm"&gt;/* heap_free_slots_max_ratio默认0.65， 总slot数乘以 0.65 就是空闲slot数的上限 */&lt;/span&gt;

  &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;

  &lt;span class="cm"&gt;/* 【裁员的slot数】 减去 【空置slot数上限】 , 再除以【每个page内单位slot数】这个单位。最后算出的heap_pages_freeable_pages 就是可以还给操作系统的page数量 */&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sweep_slots&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;max_free_slots&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;heap_pages_freeable_pages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sweep_slots&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;max_free_slots&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;HEAP_PAGE_OBJ_LIMIT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;heap_pages_freeable_pages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/* 裁员完毕后，会调这个 heap_pages_free_unused_pages函数 */&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
&lt;span class="nf"&gt;heap_pages_free_unused_pages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rb_objspace_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;objspace&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;

  &lt;span class="cm"&gt;/* 如果有退租slot数的额度*/&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;has_pages_in_tomb_heap&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;heap_allocated_pages&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;heap_page&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;heap_pages_sorted&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

            &lt;span class="cm"&gt;/* 如果还有退租slot数的额度，并且当前page是完全空置, 则退还page给操作系统 */&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;in_tomb&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;free_slots&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;total_slots&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;heap_unlink_page&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;objspace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;heap_tomb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="n"&gt;heap_page_free&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;objspace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="Q: 怎么判断哪些【员工】(Ruby对象)是无用的，需要被裁?"&gt;Q: 怎么判断哪些【员工】(&lt;code&gt;Ruby对象&lt;/code&gt;) 是无用的，需要被裁？&lt;/h2&gt;
&lt;p&gt;用标记回收算法，具体见下一节&lt;/p&gt;
&lt;h2 id="标记回收算法"&gt;标记回收算法&lt;/h2&gt;&lt;h2 id="Q: 标记回收算法的流程是怎样的?"&gt;Q: 标记回收算法的流程是怎样的？&lt;/h2&gt;
&lt;p&gt;还是以公司裁员为例子，分两个阶段：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;第一个阶段 Mark，标记有用。HR 从最顶层的总裁问起，觉得谁有用？总裁觉得副总裁 A 和副总裁 B 有用。HR 把副总裁 A 和副总裁 B 标记为有用。然后 HR 再去问副总裁 A 和副总裁 B，觉得谁有用？副总裁们觉得经理 C 和经理 D 有用........。以此类推，递归地标记有用和问下去。最后没有被问到的人，没有被标记有用的人，就可以准备裁了&lt;/li&gt;
&lt;li&gt;第二个阶段 Sweep，回收工位。找到所有未标记有用的人，裁掉，让出【工位】(&lt;code&gt;slot&lt;/code&gt;)。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;标记回收的源码也在 &lt;a href="https://github.com/ruby/ruby/blob/b43ad6b802b805cec6bee503e352d9697da8ca56/gc.c#L6723" rel="nofollow" target="_blank" title=""&gt;gc.c&lt;/a&gt;, 精简后如下&lt;/p&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* 标记obj对象有用 */&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
&lt;span class="nf"&gt;gc_mark_ptr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rb_objspace_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;objspace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;gc_mark_set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;objspace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="cm"&gt;/* 如果已经标记过，则return；否则标记成功，接着执行 */&lt;/span&gt;

  &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;

  &lt;span class="n"&gt;gc_grey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;objspace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="cm"&gt;/* 把obj推入栈中，方便之后问它引用了哪些子对象, 觉得哪些子对象有用 */&lt;/span&gt;

  &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/* 推入栈中 */&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
&lt;span class="nf"&gt;gc_grey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rb_objspace_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;objspace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;

  &lt;span class="n"&gt;push_mark_stack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;objspace&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;mark_stack&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/* 之后从栈中取出对象 */&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kr"&gt;inline&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;
&lt;span class="nf"&gt;gc_mark_stacked_objects&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rb_objspace_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;objspace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;incremental&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;

  &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pop_mark_stack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mstack&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;

     &lt;span class="cm"&gt;/* 从栈中取出obj，再递归的标记obj引用了的子对象。gc_mark_children之后又会递归调用上面的 gc_mark_ptr 函数标记子对象*/&lt;/span&gt;
     &lt;span class="n"&gt;gc_mark_children&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;objspace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="cm"&gt;/* 标记完后，对每个page调 gc_page_sweep 函数进行回收检查 */&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kr"&gt;inline&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
&lt;span class="nf"&gt;gc_sweep_page&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rb_objspace_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;objspace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rb_size_pool_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;size_pool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rb_heap_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;heap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;gc_sweep_context&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;

  &lt;span class="cm"&gt;/* page内第一个slot的地址，存在p里 */&lt;/span&gt;
  &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sweep_page&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="cm"&gt;/* page内用一个位图数组mark_bits来标记每个slot是否有用 */&lt;/span&gt;
  &lt;span class="n"&gt;bits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sweep_page&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;mark_bits&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="cm"&gt;/* 循环检查每个slot对应的位图 */&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;HEAP_PAGE_BITMAP_LIMIT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;bitset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="n"&gt;bits&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;  &lt;span class="cm"&gt;/* 把位图取反，就是没有被标记的slot */&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bitset&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

        &lt;span class="cm"&gt;/* 如果取反的位图数组有未标记的slot，调gc_sweep_plane回收 slot, 函数定义见下方 */&lt;/span&gt;
        &lt;span class="n"&gt;gc_sweep_plane&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;objspace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;heap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;uintptr_t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bitset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;BITS_BITLENGTH&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kr"&gt;inline&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
&lt;span class="nf"&gt;gc_sweep_plane&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rb_objspace_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;objspace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rb_heap_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;heap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;uintptr_t&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bits_t&lt;/span&gt; &lt;span class="n"&gt;bitset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;gc_sweep_context&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;

   &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="n"&gt;vp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;VALUE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="cm"&gt;/* 取反位图的右边最低位是1，说能原位图右边最低位是0，说明该位对应的slot没被标记，可以被回收 */&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bitset&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;

          &lt;span class="n"&gt;obj_free&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;objspace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="cm"&gt;/* 删除slot上的变量，清空slot */&lt;/span&gt;

          &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="cm"&gt;/* 循环找下一个slot，取反位图bitset右移到下一位 */&lt;/span&gt;
        &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;slot_size&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;bitset&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;slot_bits&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bitset&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="Q: 每次裁员要从头问一遍所有人， 有什么办法优化吗?"&gt;Q: 每次裁员要从头问一遍所有人，有什么办法优化吗？&lt;/h2&gt;
&lt;p&gt;用 Ruby 2.1 新增的分代回收算法，具体见下一节&lt;/p&gt;
&lt;h2 id="分代回收"&gt;分代回收&lt;/h2&gt;&lt;h2 id="Q: 分代回收算法的流程是怎样的?"&gt;Q: 分代回收算法的流程是怎样的？&lt;/h2&gt;
&lt;p&gt;还是以公司裁员打比方。分为【大裁员】(&lt;code&gt;Major/Full&lt;/code&gt;) 和【小裁员】(&lt;code&gt;Minor&lt;/code&gt;)。&lt;/p&gt;

&lt;p&gt;【大裁员】(&lt;code&gt;Major/Full&lt;/code&gt;) 还是和之前一样，问一遍所有人。&lt;/p&gt;

&lt;p&gt;【小裁员】(&lt;code&gt;Minor&lt;/code&gt;) 会默认一些【员工】(&lt;code&gt;Ruby对象&lt;/code&gt;) 有用，跳过对这些【员工】(&lt;code&gt;Ruby对象&lt;/code&gt;) 的标记和询问。&lt;/p&gt;
&lt;h2 id="Q: 哪些【员工】(Ruby对象)会默认有用?"&gt;Q: 哪些【员工】(&lt;code&gt;Ruby对象&lt;/code&gt;) 会默认有用？&lt;/h2&gt;
&lt;p&gt;被标记过 3 次的，即躲过三次裁员的【老员工】(Ruby 管这种对象叫&lt;code&gt;old object&lt;/code&gt;) 默认有用。同时，【老员工】(&lt;code&gt;old object&lt;/code&gt;) 觉得有用的子员工，也自动变为【老员工】, 形成了一个老员工帮。&lt;/p&gt;

&lt;p&gt;例如，某个员工之前躲过了两次裁员。当第三次裁员时，HR 询问该员工觉得谁有用时，发现算上目前这次，该员工已经躲过三次裁员了，就把该员工标记为【老员工】(&lt;code&gt;old object&lt;/code&gt;)。该员工觉得员工 A 和员工 B 有用，则 HR 把员工 A 和员工 B 也标记为【老员工】(&lt;code&gt;old object&lt;/code&gt;)。以此类推，员工 A 和员工 B 又觉得员工 C 和员工 D 有用，则 HR 把员工 C 和员工 D 也标记为【老员工】.....&lt;/p&gt;

&lt;p&gt;当第四次【小裁员】(&lt;code&gt;minor&lt;/code&gt;) 时，HR 默认这帮老员工有用，跳过对这些老员工的标记和询问&lt;/p&gt;
&lt;h2 id="Q: 上面的例子，如果在第三次裁员后，第四次裁员前，新招了一个新员工E，某个【老员工】(old object)觉得新员工E有用。但HR跳过了老员工的询问，不知道员工E有用，第四次裁员时误裁掉员工E。怎么办?"&gt;Q: 上面的例子，如果在第三次裁员后，第四次裁员前，新招了一个新员工 E，某个【老员工】(&lt;code&gt;old object&lt;/code&gt;) 觉得新员工 E 有用。但 HR 跳过了老员工的询问，不知道员工 E 有用，第四次裁员时误裁掉员工 E。怎么办？&lt;/h2&gt;
&lt;p&gt;【老员工】(&lt;code&gt;old object&lt;/code&gt;) 主动给 HR 说，下次裁员时，强制询问我谁有用。HR 给【老员工】(&lt;code&gt;old object&lt;/code&gt;) 标记下次【强制询问】的标签。等下次裁员结束，保住了员工 E 后，就可以清除【强制询问】的标签。&lt;/p&gt;

&lt;p&gt;Ruby 里这种主动标记强制询问的方法叫 &lt;code&gt;write barrier&lt;/code&gt;。Ruby 管这种主动标记强制询问的对象叫 &lt;code&gt;write barrier protected object&lt;/code&gt;&lt;/p&gt;
&lt;h2 id="Q: 所有的员工都会这么主动帮新员工吗?"&gt;Q: 所有的员工都会这么主动帮新员工吗？&lt;/h2&gt;
&lt;p&gt;很可惜，不会，因为要兼容第三方 c 扩展库。Ruby 给新函数加上了&lt;code&gt;write barrier&lt;/code&gt; 行为，但第三方 c 扩展使用的可能是不带&lt;code&gt;write barrier&lt;/code&gt;的老函数&lt;/p&gt;
&lt;h2 id="Q: 那怎么办?"&gt;Q: 那怎么办？&lt;/h2&gt;
&lt;p&gt;HR 把这些不主动的员工标记为【老油条】（Ruby 里叫 &lt;code&gt;write barrier unprotected object&lt;/code&gt;或者&lt;code&gt;shady&lt;/code&gt;）。就算【老油条】混成了【老员工】，HR 每次裁员时，还是要强制标记和询问【老油条】(&lt;code&gt;write barrier unprotected object/shady&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;分代回收的源码还是在 &lt;a href="https://github.com/ruby/ruby/blob/b43ad6b802b805cec6bee503e352d9697da8ca56/gc.c#L7837" rel="nofollow" target="_blank" title=""&gt;gc.c&lt;/a&gt; , 精简后如下&lt;/p&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* 标记开始函数 */&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
&lt;span class="nf"&gt;gc_marks_start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rb_objspace_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;objspace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;full_mark&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="cm"&gt;/* 是否大裁员 Major/Full */&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;full_mark&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cm"&gt;/* 大裁员 */&lt;/span&gt;
    &lt;span class="n"&gt;objspace&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;during_minor_gc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;FALSE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;SIZE_POOL_COUNT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="cm"&gt;/* 清空之前标记的【是否有用】【是否强制询问】【是否老员工】等标志 */&lt;/span&gt;
        &lt;span class="n"&gt;rgengc_mark_and_rememberset_clear&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;objspace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SIZE_POOL_EDEN_HEAP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;size_pools&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cm"&gt;/* 小裁员 */&lt;/span&gt;
    &lt;span class="n"&gt;objspace&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;during_minor_gc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TRUE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;SIZE_POOL_COUNT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

       &lt;span class="cm"&gt;/* 先把需要【强制询问】或【老油条】的对象标记询问 */&lt;/span&gt;
       &lt;span class="n"&gt;rgengc_rememberset_mark&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;objspace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SIZE_POOL_EDEN_HEAP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;size_pools&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="cm"&gt;/* 从顶层对象开始标记询问，gc_mark_roots会调用上一节源码里的 gc_mark_ptr */&lt;/span&gt;
  &lt;span class="n"&gt;gc_mark_roots&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;objspace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/* 清空各种标记位图数组 */&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
&lt;span class="nf"&gt;rgengc_mark_and_rememberset_clear&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rb_objspace_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;objspace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rb_heap_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;heap&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;heap_page&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="cm"&gt;/* mark_bits是【是否有用】的位图数组, uncollectible_bits是【是否老员工/old object】的位图数组, marking_bits是时是否【强制询问】的位图数组; pinned_bits先忽略，讲到碎片整理一节时再说 */&lt;/span&gt;
    &lt;span class="n"&gt;list_for_each&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;heap&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;pages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;page_node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;memset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;mark_bits&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;       &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HEAP_PAGE_BITMAP_SIZE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;memset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;uncollectible_bits&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HEAP_PAGE_BITMAP_SIZE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;memset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;marking_bits&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;    &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HEAP_PAGE_BITMAP_SIZE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;memset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;pinned_bits&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;     &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HEAP_PAGE_BITMAP_SIZE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/* 标记【强制询问】或【老油条】对象 */&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
&lt;span class="nf"&gt;rgengc_rememberset_mark&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rb_objspace_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;objspace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rb_heap_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;heap&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;

        &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;

        &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="err"&gt;循环检查每个&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
        &lt;span class="n"&gt;list_for_each&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;heap&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;pages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;page_node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;has_remembered_objects&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;has_uncollectible_shady_objects&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
                        &lt;span class="n"&gt;RVALUE&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

                        &lt;span class="cm"&gt;/* marking_bits 是【强制询问】的位图数组，uncollectible_bits是【老员工】的位图数组，wb_unprotected_bits 是【老油条】的位图数组 */&lt;/span&gt;
                        &lt;span class="n"&gt;bits_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;marking_bits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;marking_bits&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                        &lt;span class="n"&gt;bits_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;uncollectible_bits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;uncollectible_bits&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                        &lt;span class="n"&gt;bits_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;wb_unprotected_bits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;wb_unprotected_bits&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

                        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HEAP_PAGE_BITMAP_LIMIT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
                                &lt;span class="cm"&gt;/* bits代表(【强制询问】或者（【老员工】并且【老油条】）) */&lt;/span&gt;
                                &lt;span class="n"&gt;bits&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;marking_bits&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uncollectible_bits&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;wb_unprotected_bits&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

                                &lt;span class="cm"&gt;/* 清空【强制询问】位图数组 */&lt;/span&gt;
                                &lt;span class="n"&gt;marking_bits&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                        &lt;span class="p"&gt;}&lt;/span&gt; 

                        &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;


                        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;HEAP_PAGE_BITMAP_LIMIT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
                                &lt;span class="n"&gt;bitset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bits&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
                                &lt;span class="cm"&gt;/* 检查每个bits位图数组 */&lt;/span&gt;
                                &lt;span class="n"&gt;rgengc_rememberset_mark_plane&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;objspace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;uintptr_t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bitset&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                                &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;BITS_BITLENGTH&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                        &lt;span class="p"&gt;}&lt;/span&gt; 
                &lt;span class="p"&gt;}&lt;/span&gt; 
        &lt;span class="p"&gt;}&lt;/span&gt; 
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/* 询问有【强制询问】或【老油条】标签的对象 */&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kr"&gt;inline&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
&lt;span class="nf"&gt;rgengc_rememberset_mark_plane&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rb_objspace_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;objspace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;uintptr_t&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bits_t&lt;/span&gt; &lt;span class="n"&gt;bitset&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bitset&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bitset&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;VALUE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


                &lt;span class="cm"&gt;/* 标记询问有用的子对象，gc_mark_children 在上一节的源码里也出现过 */&lt;/span&gt;
                &lt;span class="n"&gt;gc_mark_children&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;objspace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RVALUE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;bitset&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bitset&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/* 标记obj对象有用 */&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
&lt;span class="nf"&gt;gc_mark_ptr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rb_objspace_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;objspace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;

  &lt;span class="cm"&gt;/* 检查是obj的父标对象是谁，即是谁觉得obj有用 */&lt;/span&gt;
  &lt;span class="n"&gt;rgengc_check_relation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;objspace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;gc_mark_set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;objspace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 

  &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;

  &lt;span class="n"&gt;gc_grey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;objspace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 

  &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/* 检查obj的父标对象是谁，是谁觉得obj有用 */&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
&lt;span class="nf"&gt;rgengc_check_relation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rb_objspace_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;objspace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;       
        &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="n"&gt;old_parent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;objspace&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;rgengc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parent_object&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="cm"&gt;/* 如果父标记对象是【老员工】old object */&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;old_parent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

                &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;

                &lt;span class="cm"&gt;/* 把当前对象obj也标记为【老员工】old object */&lt;/span&gt;
                &lt;span class="n"&gt;RVALUE_AGE_SET_CANDIDATE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;objspace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; 

&lt;span class="cm"&gt;/* 标记回收结束时，对page调用gc_setup_mark_bits函数 */&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
&lt;span class="nf"&gt;gc_setup_mark_bits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;heap_page&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cm"&gt;/* copy oldgen bitmap to mark bitmap */&lt;/span&gt;
    &lt;span class="cm"&gt;/* 把【老员工】uncollectible_bits位图数组复制给【是否有用】mark_bits位图数组，即老员工 old object默认有用 */&lt;/span&gt;
    &lt;span class="n"&gt;memcpy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;mark_bits&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;uncollectible_bits&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;HEAP_PAGE_BITMAP_SIZE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/* 变量a引用了变量b，觉得变量b有用时，触发writebarrier, 把变量a标记为【强制询问】 */&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt;
&lt;span class="nf"&gt;rb_gc_writebarrier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;RVALUE_OLD_P&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;RVALUE_OLD_P&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// do nothing&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="cm"&gt;/* 如果a是【老员工】(old object)，b不是【老员工】(old object), 则把a标记为【强制询问】 */&lt;/span&gt;
        &lt;span class="n"&gt;gc_writebarrier_generational&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;objspace&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="cm"&gt;/* 把变量a标记为【强制询问】 */&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
&lt;span class="nf"&gt;gc_writebarrier_generational&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rb_objspace_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;objspace&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;

  &lt;span class="n"&gt;rgengc_remember&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;objspace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/* 把obj标记为【强制询问】 */&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;
&lt;span class="nf"&gt;rgengc_remember&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rb_objspace_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;objspace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;rgengc_remembersetbits_set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;objspace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/* 把obj标记为【强制询问】 */&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;
&lt;span class="nf"&gt;rgengc_remembersetbits_set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rb_objspace_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;objspace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;heap_page&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GET_HEAP_PAGE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;bits_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;bits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;marking_bits&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt; &lt;span class="cm"&gt;/* 【强制询问】的位图数组是page内的 marking_bits */&lt;/span&gt;

   &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;

   &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;has_remembered_objects&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TRUE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="n"&gt;MARK_IN_BITMAP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bits&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="cm"&gt;/* 把obj对应的marking_bits位置标为1 */&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;TRUE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="Q: 【小裁员】(minor)跳过【老员工】(old object)减少了标记的时间。但【大裁员】(major/full)依然要问一遍所有人，有什么办法优化【大裁员】(major/full)吗?"&gt;Q: 【小裁员】(&lt;code&gt;minor&lt;/code&gt;) 跳过【老员工】(&lt;code&gt;old object&lt;/code&gt;) 减少了标记的时间。但【大裁员】(&lt;code&gt;major/full&lt;/code&gt;) 依然要问一遍所有人，有什么办法优化【大裁员】(&lt;code&gt;major/full&lt;/code&gt;) 吗？&lt;/h2&gt;
&lt;p&gt;用 Ruby 2.2 新加的增量回收算法，具体见下一节&lt;/p&gt;
&lt;h2 id="增量回收"&gt;增量回收&lt;/h2&gt;&lt;h2 id="Q: 增量回收算法是怎么优化【大裁员】(major/full) 流程的?"&gt;Q: 增量回收算法是怎么优化【大裁员】(&lt;code&gt;major/full&lt;/code&gt;) 流程的？&lt;/h2&gt;
&lt;p&gt;【大裁员】(&lt;code&gt;major/full&lt;/code&gt;) 需要问一遍所有人，这个客观事实是改不了的。增量回收算法的优化思路是：通过增量标记询问，尽量减少对员工们正常干活的阻塞。&lt;/p&gt;

&lt;p&gt;在 Ruby 2.2 增量回收算法出现前。【大裁员】(&lt;code&gt;major/full&lt;/code&gt;) 时，员工们都必须停下手头的工作，等裁员结果出来，才能继续工作。&lt;/p&gt;

&lt;p&gt;Ruby 2.2 增量回收算法出来后。除了原先【已标记】(Ruby 叫&lt;code&gt;black object&lt;/code&gt;) 和【未标记】(Ruby 叫&lt;code&gt;white object&lt;/code&gt;) 这两种状态，HR 新增了一种【询问中】(Ruby 叫&lt;code&gt;grey object&lt;/code&gt;) 状态。&lt;/p&gt;

&lt;p&gt;例如，第一天 HR 把总裁标记为【询问中】(&lt;code&gt;grey object&lt;/code&gt;)，总裁觉得副总裁 A 和副总裁 B 有用。HR 把副总裁 A 和副总裁 B 标记为【询问中】，把总裁标记为【已标记】(&lt;code&gt;black object&lt;/code&gt;)。就结束了第一天的标记，员工可以接着干活。
第二天，HR 在询问副总裁 A 和副总裁 B。副总裁们觉得经理 C 和经理 D 有用，HR 把经理 C 和经理 D 标记为【询问中】(&lt;code&gt;grey object&lt;/code&gt;), 把副总裁们标记为【已标记】(&lt;code&gt;black object&lt;/code&gt;)。结束了第二天的标记，员工可以接着干活。以此循环递归，每天增量的询问一些【询问中】的员工，直到没有【询问中】(&lt;code&gt;grey object&lt;/code&gt;) 的员工。只剩【已标记】和【未标记】的员工。&lt;/p&gt;
&lt;h2 id="Q: 和分代回收时同样的问题：如果某个员工A被标记为【已标记】(black object),  然后新招了一个员工B，员工A觉得员工B有用。但因为HR不会再询问员工A了，不知道员工B有用，导致员工B被误裁，怎么办？"&gt;Q: 和分代回收时同样的问题：如果某个员工 A 被标记为【已标记】(&lt;code&gt;black object&lt;/code&gt;),  然后新招了一个员工 B，员工 A 觉得员工 B 有用。但因为 HR 不会再询问员工 A 了，不知道员工 B 有用，导致员工 B 被误裁，怎么办？&lt;/h2&gt;
&lt;p&gt;跟分代回收类似的解决方案，&lt;code&gt;writebarrier&lt;/code&gt;。员工 A 主动把跟 HR 说把员工 B 从【未标记】(&lt;code&gt;white object&lt;/code&gt;) 改为【询问中】(&lt;code&gt;grey object&lt;/code&gt;)。实际上分代回收的【强制询问】和增量回收的【询问中】用的是同一个标记，同一个位图数组 &lt;code&gt;marking_bits&lt;/code&gt;，因为两者的作用一致，都是让 HR 下次询问自己一次。&lt;/p&gt;
&lt;h2 id="Q: 所有的员工都会主动帮新员工吗？"&gt;Q: 所有的员工都会主动帮新员工吗？&lt;/h2&gt;
&lt;p&gt;很可惜，不会，原因和分代回收时一样，为了兼容第三方 c 扩展库。必须用别的办法对付【老油条】(&lt;code&gt;writebarrier unprotected object&lt;/code&gt;)&lt;/p&gt;
&lt;h2 id="Q：那怎么办?"&gt;Q：那怎么办？&lt;/h2&gt;
&lt;p&gt;HR 在【大裁员】(&lt;code&gt;major/full&lt;/code&gt;) 最终标记结束前，会最后再问一遍所有的【老油条】(&lt;code&gt;writebarrier unprotected object&lt;/code&gt;)，以防漏掉了哪个有用的员工。&lt;/p&gt;

&lt;p&gt;增量回收的源码还是在 &lt;a href="https://github.com/ruby/ruby/blob/b43ad6b802b805cec6bee503e352d9697da8ca56/gc.c#L1693" rel="nofollow" target="_blank" title=""&gt;gc.c&lt;/a&gt;&lt;/p&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* 标记了有用 mark_bits, 没标记强询问 marking_bits 的就是black object*/&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kr"&gt;inline&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;
&lt;span class="nf"&gt;RVALUE_BLACK_P&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;RVALUE_MARKED&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;RVALUE_MARKING&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;#if 0
/* 标记了有用 mark_bits, 同时标记了下次强制询问 marking_bits的就是grey object */
static inline int
RVALUE_GREY_P(VALUE obj)
{
    return RVALUE_MARKED(obj) &amp;amp;&amp;amp; RVALUE_MARKING(obj);
}
#endif
&lt;/span&gt;
&lt;span class="cm"&gt;/* 还没被标记有用mark_bits的就是white object */&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kr"&gt;inline&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;
&lt;span class="nf"&gt;RVALUE_WHITE_P&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;RVALUE_MARKED&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;FALSE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/* 增量回收中，a觉得b有用的writebarrier函数 */&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
&lt;span class="nf"&gt;gc_writebarrier_incremental&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rb_objspace_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;objspace&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;gc_report&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;objspace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"gc_writebarrier_incremental: [LG] %p -&amp;gt; %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;obj_info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RVALUE_BLACK_P&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RVALUE_WHITE_P&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;RVALUE_WB_UNPROTECTED&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="cm"&gt;/* 变量a是black，变量b是white时，把变量b标记为grey */&lt;/span&gt;
                &lt;span class="n"&gt;gc_report&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;objspace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"gc_writebarrier_incremental: [IN] %p -&amp;gt; %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;obj_info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
                &lt;span class="n"&gt;gc_mark_from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;objspace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RVALUE_OLD_P&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;RVALUE_OLD_P&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;RVALUE_WB_UNPROTECTED&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="cm"&gt;/* 变量a是老对象，变量b是新对象时，把变量b标记为老对象 */&lt;/span&gt;
                &lt;span class="n"&gt;gc_report&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;objspace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"gc_writebarrier_incremental: [GN] %p -&amp;gt; %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;obj_info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
                &lt;span class="n"&gt;RVALUE_AGE_SET_OLD&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;objspace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RVALUE_BLACK_P&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="cm"&gt;/* 如果变量b是black时，改为grey，下次强制询问 */&lt;/span&gt;
                    &lt;span class="n"&gt;gc_grey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;objspace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;gc_report&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;objspace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"gc_writebarrier_incremental: [LL] %p -&amp;gt; %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;obj_info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
                &lt;span class="cm"&gt;/* 变量b是【老油条】writebarrier unprotected, 标记b为【老员工】old object */&lt;/span&gt;
                &lt;span class="n"&gt;gc_remember_unprotected&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;objspace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/* 标记结束前调用的函数 */&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;
&lt;span class="nf"&gt;gc_marks_finish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rb_objspace_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;objspace&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="cp"&gt;#if GC_ENABLE_INCREMENTAL_MARK
&lt;/span&gt;  &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;

  &lt;span class="cm"&gt;/* 增量标记结束前，再询问一遍【老油条】writebarrier unprotected object, 以防漏掉有用变量 */&lt;/span&gt;
  &lt;span class="cm"&gt;/* gc_marks_wb_unprotected_objects 函数会再询问一遍所有writebarrier unprotected object */&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;SIZE_POOL_COUNT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;gc_marks_wb_unprotected_objects&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;objspace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SIZE_POOL_EDEN_HEAP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;size_pools&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="cp"&gt;#endif
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="Q: 所以一个【员工】(Ruby对象)总共有哪些标记?"&gt;Q: 所以一个【员工】(&lt;code&gt;Ruby对象&lt;/code&gt;) 总共有哪些标记？&lt;/h2&gt;
&lt;p&gt;是否有用 (&lt;code&gt;mark_bits&lt;/code&gt;), 是否老员工 (&lt;code&gt;uncollectible_bits&lt;/code&gt;), 是否老油条 (&lt;code&gt;wb_unprotected_bits&lt;/code&gt;), 是否下次强制询问 (&lt;code&gt;marking_bits&lt;/code&gt;), 是否钉子户 (&lt;code&gt;pinned_bits&lt;/code&gt;)&lt;/p&gt;
&lt;h2 id="Q: 裁员完后，不换一下工位，让员工间坐得更近，空出更多的【办公室】(page)吗?"&gt;Q: 裁员完后，不换一下工位，让员工间坐得更近，空出更多的【办公室】(&lt;code&gt;page&lt;/code&gt;) 吗？&lt;/h2&gt;
&lt;p&gt;可以，Ruby 2.7 加了碎片整理算法，详情见下一节&lt;/p&gt;
&lt;h2 id="碎片整理"&gt;碎片整理&lt;/h2&gt;&lt;h2 id="Q: 碎片整理算法的流程是怎样的?"&gt;Q: 碎片整理算法的流程是怎样的？&lt;/h2&gt;
&lt;p&gt;以公司裁员打比方，必须先进行一次【大裁员】(&lt;code&gt;major/full&lt;/code&gt;) 后，再执行如下两个阶段：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;第一阶段，转移工位。两个 HR，一个负责在左边，从左往右找空出的【工位】(&lt;code&gt;slot&lt;/code&gt;), 另一个负责在右边，从右往左找【员工】(&lt;code&gt;Ruby对象&lt;/code&gt;), 各自找到合适的【工位】(&lt;code&gt;slot&lt;/code&gt;) 和【员工】(&lt;code&gt;Ruby对象&lt;/code&gt;) 后，把员工转移到新工位，并在原工位上留下新工位的字条。以此循环，直到找不到合适的工位或员工，两个 HR 碰到了一起。&lt;/li&gt;
&lt;li&gt;第二阶段，更新引用。通知所有员工，根据原工位留的字条，记一下对应员工的新工位。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;具体例子见下图&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;6个工位，4个员工，A和B记得C和D在5和6号工位
                     ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─        
                        Before Compaction   │       
                     └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─        
               ┌─────┬─────┬─────┬─────┬─────┬─────┐
 Slot Index    │  1  │  2  │  3  │  4  │  5  │  6  │
               ├─────┼─────┼─────┼─────┼─────┼─────┤
Slot Contents  │  A  │  B  │Empty│Empty│  C  │  D  │
               │     │     │     │     │     │     │
               └─────┴─────┴─────┴─────┴─────┴─────┘
                  │     │                 ▲     ▲   
                  │     │                 │     │   
                  └─────┼─────────────────┘     │   
                        └───────────────────────┘  
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;第一阶段，C和D转移工位到3和4后，在原工位留下字条
                     ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─        
                         After Compaction   │       
                     └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─        
               ┌─────┬─────┬─────┬─────┬─────┬─────┐
 Slot Index    │  1  │  2  │  3  │  4  │  5  │  6  │
               ├─────┼─────┼─────┼─────┼─────┼─────┤
Slot Contents  │  A  │  B  │  D  │  C  │MOVED│MOVED│
               │     │     │     │     │TO 4 │TO 3 │
               └─────┴─────┴─────┴─────┴─────┴─────┘
                  │     │                 ▲     ▲   
                  │     │                 │     │   
                  └─────┼─────────────────┘     │   
                        └───────────────────────┘ 
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;第二阶段，员工A和B根据字条，更新一下记的员工C和D的工位位置
                     ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─        
                        After Ref Updates   │       
                     └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─        
               ┌─────┬─────┬─────┬─────┬─────┬─────┐
 Slot Index    │  1  │  2  │  3  │  4  │  5  │  6  │
               ├─────┼─────┼─────┼─────┼─────┼─────┤
Slot Contents  │  A  │  B  │  D  │  C  │MOVED│MOVED│
               │     │     │     │     │TO 4 │TO 3 │
               └─────┴─────┴─────┴─────┴─────┴─────┘
                  │     │     ▲     ▲               
                  │     │     │     │               
                  └─────┼─────┼─────┘               
                        └─────┘   
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="Q: 所有员工都会乖乖换工位吗？"&gt;Q: 所有员工都会乖乖换工位吗？&lt;/h2&gt;
&lt;p&gt;很可惜，不会，原因和分代回收一样，为了兼容第三方 c 扩展库。碎片整理时会跳过这些【钉子户】(Ruby 内叫&lt;code&gt;pinned object&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;由于原理很简单，更多的难点是在为了升级第三方 c 扩展库，而提供的&lt;code&gt;rb_gc_location&lt;/code&gt;, &lt;code&gt;rb_data_type_struct中的dcompact回调函数指针&lt;/code&gt;等，本节省略源码解析&lt;/p&gt;
&lt;h2 id="Q: 碎片整理除了空出更多的【办公室】(page), 还有别的好处吗?"&gt;Q: 碎片整理除了空出更多的【办公室】(&lt;code&gt;page&lt;/code&gt;), 还有别的好处吗？&lt;/h2&gt;
&lt;p&gt;有，根据局部性原理，能优化读取速度。&lt;/p&gt;
&lt;h2 id="局部性原理"&gt;局部性原理&lt;/h2&gt;&lt;h2 id="Q: 什么是局部性原理？"&gt;Q: 什么是局部性原理？&lt;/h2&gt;
&lt;p&gt;cpu 读取内存数据时，如果是读取相邻的顺序数据，更有机会被 L1，L2，L3 缓存命中，提高性能。&lt;/p&gt;
&lt;h2 id="Q: 除了碎片整理，Ruby垃圾回收中还有哪些优化用到了局部性原理吗？"&gt;Q: 除了碎片整理，Ruby 垃圾回收中还有哪些优化用到了局部性原理吗？&lt;/h2&gt;
&lt;p&gt;Ruby 3.1 新加了 Variable Width Allocation, 但跟 jit 一样默认是关闭状态的实验特性。&lt;/p&gt;

&lt;p&gt;原先一个【工位】(&lt;code&gt;slot&lt;/code&gt;) 只有 40 字节，额外数据需要存储在外部 heap。Variable Width Allocation 的原理是，把【工位】(&lt;code&gt;slot&lt;/code&gt;) 扩大到 40 字节，80 字节，160 字节，320 字节四种。虽然可能造成一定的空间浪费，但根据局部性原理，提高了性能。&lt;/p&gt;

&lt;p&gt;详情见 Variable Width Allocation 的 redmine &lt;/p&gt;

&lt;p&gt;&lt;a href="https://bugs.ruby-lang.org/issues/18045" rel="nofollow" target="_blank"&gt;https://bugs.ruby-lang.org/issues/18045&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://bugs.ruby-lang.org/issues/18239" rel="nofollow" target="_blank"&gt;https://bugs.ruby-lang.org/issues/18239&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="Q: 除了垃圾回收，Ruby中还有哪些优化用到了局部性原理吗？"&gt;Q: 除了垃圾回收，Ruby 中还有哪些优化用到了局部性原理吗？&lt;/h2&gt;
&lt;p&gt;Ruby 2.4 中用局部性原理优化了 Hash 的实现，详情见下面文章中 Hash changes 的科普
&lt;a href="https://blog.heroku.com/ruby-2-4-features-hashes-integers-rounding" rel="nofollow" target="_blank"&gt;https://blog.heroku.com/ruby-2-4-features-hashes-integers-rounding&lt;/a&gt;&lt;/p&gt;</description>
      <author>zhuoerri</author>
      <pubDate>Tue, 26 Apr 2022 12:32:28 +0800</pubDate>
      <link>https://ruby-china.org/topics/42344</link>
      <guid>https://ruby-china.org/topics/42344</guid>
    </item>
    <item>
      <title>YJIT 打算用 Rust 重写</title>
      <description>&lt;p&gt;&lt;a href="https://bugs.ruby-lang.org/issues/18481" rel="nofollow" target="_blank"&gt;https://bugs.ruby-lang.org/issues/18481&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;以后 &lt;del&gt;安装 ruby&lt;/del&gt; 从源码编译安装 ruby 时，可能需要先安装 rustc, cargo 才能开启 YJIT 了&lt;/p&gt;</description>
      <author>zhuoerri</author>
      <pubDate>Fri, 18 Feb 2022 13:49:11 +0800</pubDate>
      <link>https://ruby-china.org/topics/42148</link>
      <guid>https://ruby-china.org/topics/42148</guid>
    </item>
    <item>
      <title>Ruby YJIT 原理浅析</title>
      <description>&lt;h2 id="前言"&gt;前言&lt;/h2&gt;
&lt;p&gt;本人水平有限，如有错误，欢迎指正与补充。&lt;/p&gt;

&lt;p&gt;YJIT 是 shopify 公司开发的 jit, 已于去年合并到了 ruby 源码。
本文会结合源码从 字节码，YJIT，到背后的 Lazy Basic Block Versioning (LBBV) 概念进行浅析，最后个人感叹一下软件缓存。&lt;/p&gt;
&lt;h2 id="字节码"&gt;字节码&lt;/h2&gt;
&lt;p&gt;Ruby 自 1.9 版本开始，代码执行过程变为 &lt;code&gt;Ruby源码&lt;/code&gt; -&amp;gt; &lt;code&gt;字节码&lt;/code&gt; -&amp;gt; &lt;code&gt;YARV虚拟机&lt;/code&gt; 。
可用 &lt;code&gt;RubyVM::InstructionSequence.disasm&lt;/code&gt; 查看生成的字节码，如下代码&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="no"&gt;RubyVM&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;InstructionSequence&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;disasm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:get&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;Arg&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;Arg&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="mo"&gt;0000&lt;/span&gt; &lt;span class="n"&gt;getlocal_WC_0&lt;/span&gt;                          &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;                     &lt;span class="p"&gt;(&lt;/span&gt;   &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="no"&gt;LiCa&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="mo"&gt;0002&lt;/span&gt; &lt;span class="n"&gt;getlocal_WC_0&lt;/span&gt;                          &lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="mo"&gt;0004&lt;/span&gt; &lt;span class="n"&gt;opt_aref&lt;/span&gt;                               &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;calldata!mid&lt;/span&gt;&lt;span class="ss"&gt;:[]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;argc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;ARGS_SIMPLE&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="mo"&gt;0006&lt;/span&gt; &lt;span class="n"&gt;leave&lt;/span&gt;                                                            &lt;span class="p"&gt;(&lt;/span&gt;   &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="no"&gt;Re&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如上，总共生成了 4 个字节码：第一个 &lt;code&gt;getlocal_WC_0&lt;/code&gt; 获取变量 obj , 第二个&lt;code&gt;getlocal_WC_0&lt;/code&gt;获取变量 idx, 第三个&lt;code&gt;opt_aref&lt;/code&gt;代表源码里的 [], 第四个&lt;code&gt;leave&lt;/code&gt;代表 return。ruby 定义的所有字节码及对应的 c 源码，可在 &lt;a href="https://github.com/ruby/ruby/blob/v3_1_0/insns.def" rel="nofollow" target="_blank" title=""&gt;insns.def&lt;/a&gt;里查看，最后一个字节码是关于 42 的梗&lt;/p&gt;

&lt;p&gt;本文主要讲 YJIT，对于 YARV 虚拟机感兴趣的同学推荐&lt;a href="https://patshaughnessy.net/ruby-under-a-microscope" rel="nofollow" target="_blank" title=""&gt;ruby-under-a-microscope(Ruby 原理剖析)&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="JIT"&gt;JIT&lt;/h2&gt;
&lt;p&gt;YARV 虚拟机执行字节码前，会尝试先用 JIT 来执行，c 源码在&lt;a href="https://github.com/ruby/ruby/blob/fb4df44d1670e9d25aef6b235a7281199a177edb/vm.c#L2210" rel="nofollow" target="_blank" title=""&gt;vm.c&lt;/a&gt;, 精简后如下&lt;/p&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;VALUE&lt;/span&gt;
&lt;span class="nf"&gt;vm_exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rb_execution_context_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;mjit_enable_p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;mjit_enable_p&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mjit_exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ec&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;Qundef&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* 没有启动jit，或者jit的执行结果mjit_exec是不成功 Qundef， 则回到虚拟机正常执行 */&lt;/span&gt;
            &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vm_exec_core&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;initial&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;goto&lt;/span&gt; &lt;span class="n"&gt;vm_loop_start&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;mjit_exec 会尝试构造一个函数指针，然后执行该函数。源码在 &lt;a href="https://github.com/ruby/ruby/blob/fb4df44d1670e9d25aef6b235a7281199a177edb/mjit.h#L143" rel="nofollow" target="_blank" title=""&gt;mjit.h&lt;/a&gt;, 精简后如下&lt;/p&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kr"&gt;inline&lt;/span&gt; &lt;span class="n"&gt;VALUE&lt;/span&gt;
&lt;span class="nf"&gt;mjit_exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rb_execution_context_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ec&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;

  &lt;span class="n"&gt;yjit_enabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rb_yjit_enabled_p&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;yjit_enabled&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;mjit_call_p&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;total_calls&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;rb_yjit_call_threshold&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* 如果启动了yjit 并且这段字节码的总执行次数达到了yjit的限制，默认10次，则yjit尝试编译字节码 */&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;rb_yjit_compile_iseq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;iseq&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ec&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  &lt;span class="cm"&gt;/* YJIT编译字节码，如果编译失败，返回Qundef */&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Qundef&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;

  &lt;span class="n"&gt;mjit_func_t&lt;/span&gt; &lt;span class="n"&gt;func&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;jit_func&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="cm"&gt;/* 如果YJIT编译成功，获取函数指针 */&lt;/span&gt;
  &lt;span class="c1"&gt;// ec -&amp;gt; RDI         按照惯例，ec会存入RDI寄存器, 原由见下方的 yjit_entry_prologue 函数&lt;/span&gt;
  &lt;span class="c1"&gt;// cfp -&amp;gt; RSI        按照惯例, cfp会存入RSI寄存器, 原由见下方的 yjit_entry_prologue 函数&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ec&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;cfp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="cm"&gt;/* 执行函数指针对应的函数 */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;剩下的工作，就是 YJIT 的 rb_yjit_compile_iseq 函数如何编译字节码，并返回一个函数指针？&lt;/p&gt;
&lt;h2 id="YJIT"&gt;YJIT&lt;/h2&gt;
&lt;p&gt;YJIT 为大部分字节码，注册了一个处理函数，例如一开始的 &lt;code&gt;opt_aref&lt;/code&gt; 字节码，在 &lt;a href="https://github.com/ruby/ruby/blob/fb4df44d1670e9d25aef6b235a7281199a177edb/yjit_codegen.c#L4995" rel="nofollow" target="_blank" title=""&gt;yjit_codegen.c&lt;/a&gt; 中注册了 gen_opt_aref 处理函数，精简如下&lt;/p&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt;
&lt;span class="nf"&gt;yjit_init_codegen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;
   &lt;span class="n"&gt;yjit_reg_op&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BIN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opt_neq&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;gen_opt_neq&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="n"&gt;yjit_reg_op&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BIN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opt_aref&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;gen_opt_aref&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="n"&gt;yjit_reg_op&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BIN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opt_aset&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;gen_opt_aset&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;gen_opt_aref 处理函数，用手动编写汇编机器码的方式，在内存中写入 X86 汇编代码，精简如下&lt;/p&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;codegen_status_t&lt;/span&gt;
&lt;span class="nf"&gt;gen_opt_aref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jitstate_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;jit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctx_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;codeblock_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CLASS_OF&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;comptime_recv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;rb_cArray&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;RB_FIXNUM_P&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;comptime_idx&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* 如果参数类型分别是 Array和Fixnum */&lt;/span&gt;
    &lt;span class="n"&gt;mov&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;RDI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;recv_opnd&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="cm"&gt;/* 生成mov 汇编语句 */&lt;/span&gt;
    &lt;span class="n"&gt;sar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;REG1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;imm_opnd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="cm"&gt;/* 生成sar汇编语句 */&lt;/span&gt;
    &lt;span class="n"&gt;mov&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;RSI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;REG1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="cm"&gt;/* 生成 mov 汇编语句 */&lt;/span&gt;
    &lt;span class="n"&gt;call_ptr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;REG0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;rb_ary_entry_internal&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="cm"&gt;/* 生成call汇编语句，调用实际的rb_ary_entry_internal函数 */&lt;/span&gt;

    &lt;span class="n"&gt;x86opnd_t&lt;/span&gt; &lt;span class="n"&gt;stack_ret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctx_stack_push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TYPE_UNKNOWN&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;mov&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stack_ret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;RAX&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="cm"&gt;/* 生成mov汇编语句，把 rb_ary_entry_internal 的返回值(RAX寄存器的值)存入YARV虚拟机的栈顶 */&lt;/span&gt;

    &lt;span class="n"&gt;jit_jump_to_next_insn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="cm"&gt;/* 生成jmp汇编语句，继续执行后续的字节码生成的汇编语句 */&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;YJIT_END_BLOCK&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CLASS_OF&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;comptime_recv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;rb_cHash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="cm"&gt;/* 参数类型是Hash， 生成对应汇编语句，省略 */&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cm"&gt;/* 参数类型是Proc, lambda 等, 生成对应汇编语句，省略 */&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;  

  &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;除了上面的 mov, sar, call_ptr, 其他完整的汇编机器码写入内存的函数，在&lt;a href="https://github.com/ruby/ruby/blob/v3_1_0/yjit_asm.c" rel="nofollow" target="_blank" title=""&gt;yjit_asm.c&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;可想而之，YJIT 的 rb_yjit_compile_iseq 就是把字节码对应的汇编机器码写入内存，并把该内存地址作为函数指针返回了。完整代码入口见&lt;a href="https://github.com/ruby/ruby/blob/fb4df44d1670e9d25aef6b235a7281199a177edb/yjit_iface.c#L484" rel="nofollow" target="_blank" title=""&gt;yjit_iface.c&lt;/a&gt;, 精简如下&lt;/p&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;bool&lt;/span&gt;
&lt;span class="nf"&gt;rb_yjit_compile_iseq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;rb_iseq_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;iseq&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rb_execution_context_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ec&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;
    &lt;span class="n"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;success&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;code_ptr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gen_entry_point&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;iseq&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ec&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="cm"&gt;/* 获取汇编代码的内存地址, 详情见下方函数 */&lt;/span&gt;

    &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code_ptr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;iseq&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;jit_func&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;yjit_func_t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;code_ptr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="cm"&gt;/* 把汇编代码的内存地址存入函数指针 */&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;success&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="nf"&gt;gen_entry_point&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;rb_iseq_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;iseq&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;uint32_t&lt;/span&gt; &lt;span class="n"&gt;insn_idx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rb_execution_context_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ec&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;

  &lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;code_ptr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;yjit_entry_prologue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;iseq&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="cm"&gt;/* 生成初始汇编代码，把相关寄存器存入栈中, 返回初始内存地址, 详情见下方函数 */&lt;/span&gt;

  &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;

  &lt;span class="n"&gt;block_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gen_block_version&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;blockid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;DEFAULT_CTX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ec&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="cm"&gt;/* 根据字节码，生成后续的汇编代码。目前先省略,后续谈到LBBV的branch_stub_hit函数时，会再深入 */&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;code_ptr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="nf"&gt;yjit_entry_prologue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;codeblock_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;rb_iseq_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;iseq&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;

    &lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;code_ptr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cb_get_ptr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;write_pos&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="cm"&gt;/* 获取初始内存地址 */&lt;/span&gt;
    &lt;span class="n"&gt;ADD_COMMENT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"yjit entry"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;REG_CFP&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="cm"&gt;/* 在内存中写入 push 汇编代码 */&lt;/span&gt;
    &lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;REG_EC&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="cm"&gt;/* 在内存中写入 push 汇编代码 */&lt;/span&gt;
    &lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;REG_SP&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="cm"&gt;/* 在内存中写入 push 汇编代码 */&lt;/span&gt;

    &lt;span class="n"&gt;mov&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;REG_EC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;C_ARG_REGS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt; &lt;span class="cm"&gt;/* mov汇编代码, 从C_ARG_REGS[0]即RDI寄存器, 获取传入的参数EC, 存入REG_EC寄存器 */&lt;/span&gt;
    &lt;span class="n"&gt;mov&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;REG_CFP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;C_ARG_REGS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt; &lt;span class="cm"&gt;/* mov汇编代码, 从 C_ARG_REGS[0]即RSI寄存器, 获取传入的参数CFP, 存入REG_CFP寄存器 */&lt;/span&gt;

    &lt;span class="cm"&gt;/* 把 cfp-&amp;gt;sp 地址, 存入 REG_SP寄存器 */&lt;/span&gt;
    &lt;span class="n"&gt;mov&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;REG_SP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;member_opnd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;REG_CFP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rb_control_frame_t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sp&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="cm"&gt;/* 把return相关的leave_exit_code汇编代码，存入 cfp-&amp;gt;jit_return 函数指针 */&lt;/span&gt;
    &lt;span class="n"&gt;mov&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;REG0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;const_ptr_opnd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;leave_exit_code&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="n"&gt;mov&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;member_opnd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;REG_CFP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rb_control_frame_t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;jit_return&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;REG0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;code_ptr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="cm"&gt;/* 返回初始内存地址 */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="Lazy Basic Block Versioning (LBBV)"&gt;Lazy Basic Block Versioning (LBBV)&lt;/h2&gt;
&lt;p&gt;如果只是把字节码手动转成汇编代码执行，那和直接让 YARV 虚拟机执行字节码没什么区别;因此 YJIT 额外用了名为 Lazy Basic Block Versioning(LBBV) 的优化方法，详情见 YJIT 作者发表的&lt;a href="https://arxiv.org/pdf/1411.0352v2.pdf" rel="nofollow" target="_blank" title=""&gt;LBBV 论文&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="Lazy"&gt;Lazy&lt;/h3&gt;
&lt;p&gt;Lazy Basic Block Versioning 中的 Lazy 代表懒生成，类似 ActiveRecord 中的 where 懒加载，只有在调用 to_a, each 等语句时才会真正触发 sql。YJIT 一开始生成的是一段 stub(空壳汇编代码)，只有空壳代码被执行时，才继续生成真正的汇编代码。用 ruby 举例的话，类似如下代码&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;time_consuming_operation&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;respond_to?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:real_time_consuming_operation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:real_time_consuming_operation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="n"&gt;stub&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;stub&lt;/span&gt;
  &lt;span class="n"&gt;define_method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:real_time_consuming_operation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;# time consuming operations&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="n"&gt;time_consuming_operation&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
  &lt;span class="n"&gt;time_consuming_operation&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;
  &lt;span class="c1"&gt;# other operation&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如上，如果执行时 if false, time_consuming_operation 暂时不被真正执行，则暂时不会生成实际的 real_time_consuming_operation 方法。YJIT 用懒生成的方式节省了汇编代码大小，省略了暂时不需要真正执行的汇编代码。还是以&lt;code&gt;opt_aref&lt;/code&gt; 字节码对应的处理函数&lt;a href="https://github.com/ruby/ruby/blob/fb4df44d1670e9d25aef6b235a7281199a177edb/yjit_codegen.c#L2181" rel="nofollow" target="_blank" title=""&gt;gen_opt_aref 源码&lt;/a&gt;为例子，精简后如下&lt;/p&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;codegen_status_t&lt;/span&gt;
&lt;span class="nf"&gt;gen_opt_aref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jitstate_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;jit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctx_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;codeblock_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;

  &lt;span class="cm"&gt;/* 如果是初次编译字节码，而不是真的执行到了对应的汇编代码。则调用defer_compilation 生成空壳汇编代码 */&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;jit_at_current_insn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jit&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;defer_compilation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="cm"&gt;/* 生成空壳汇编代码，详情见下方函数 */&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;YJIT_END_BLOCK&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="cm"&gt;/* 否则执行下面的语句，生成实际的汇编代码 */&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CLASS_OF&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;comptime_recv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;rb_cArray&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;RB_FIXNUM_P&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;comptime_idx&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* 如果参数类型分别是 Array和Fixnum */&lt;/span&gt;
    &lt;span class="n"&gt;mov&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;RDI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;recv_opnd&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="cm"&gt;/* 生成mov 汇编语句 */&lt;/span&gt;
    &lt;span class="n"&gt;sar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;REG1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;imm_opnd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="cm"&gt;/* 生成sar汇编语句 */&lt;/span&gt;
    &lt;span class="n"&gt;mov&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;RSI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;REG1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="cm"&gt;/* 生成 mov 汇编语句 */&lt;/span&gt;
    &lt;span class="n"&gt;call_ptr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;REG0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;rb_ary_entry_internal&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="cm"&gt;/* 生成call汇编语句，调用实际的rb_ary_entry_internal函数 */&lt;/span&gt;

    &lt;span class="n"&gt;x86opnd_t&lt;/span&gt; &lt;span class="n"&gt;stack_ret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctx_stack_push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TYPE_UNKNOWN&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;mov&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stack_ret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;RAX&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="cm"&gt;/* 生成mov汇编语句，把 rb_ary_entry_internal 的返回值(RAX寄存器的值)存入YARV虚拟机的栈顶 */&lt;/span&gt;

    &lt;span class="n"&gt;jit_jump_to_next_insn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="cm"&gt;/* 生成jmp汇编语句，继续执行后续的字节码生成的汇编语句 */&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;YJIT_END_BLOCK&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CLASS_OF&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;comptime_recv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;rb_cHash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="cm"&gt;/* 参数类型是Hash， 生成对应汇编语句，省略 */&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cm"&gt;/* 参数类型是Proc, lambda 等, 生成对应汇编语句，省略 */&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; 

  &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
&lt;span class="nf"&gt;defer_compilation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;jitstate_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;jit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ctx_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;cur_ctx&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;

  &lt;span class="cm"&gt;/* 调用get_branch_target 获取stub空壳汇编代码的内存地址, 详情见下方函数 */&lt;/span&gt;
  &lt;span class="n"&gt;branch&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;dst_addrs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_branch_target&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;branch&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;targets&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;next_ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;branch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="nf"&gt;get_branch_target&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;blockid_t&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;ctx_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;branch_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;branch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kt"&gt;uint32_t&lt;/span&gt; &lt;span class="n"&gt;target_idx&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;

  &lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;stub_addr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cb_get_ptr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ocb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ocb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;write_pos&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="n"&gt;mov&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ocb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;C_ARG_REGS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;REG_EC&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="cm"&gt;/* 生成mov汇编代码，传REG_EC参数给branch_stub_hit */&lt;/span&gt;
  &lt;span class="n"&gt;mov&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ocb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;C_ARG_REGS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;imm_opnd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target_idx&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="cm"&gt;/* 生成mov汇编代码，传target_idx参数给branch_stub_hit */&lt;/span&gt;
  &lt;span class="n"&gt;mov&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ocb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;C_ARG_REGS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;const_ptr_opnd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;branch&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="cm"&gt;/* 生成mov汇编代码，传branch指针参数给branch_stub_hit */&lt;/span&gt;
  &lt;span class="n"&gt;call_ptr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ocb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;REG0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;branch_stub_hit&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="cm"&gt;/*  生成call汇编代码，调用branch_stub_hit函数, 详情见下方函数 */&lt;/span&gt;

  &lt;span class="n"&gt;jmp_rm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ocb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;RAX&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="cm"&gt;/* 生成jmp汇编代码，跳转到branch_stub_hit的返回值地址 */&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;stub_addr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/* 如果该函数被执行，说明执行时对应的空壳汇编代码也被执行，需要生成实际的汇编代码 */&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="nf"&gt;branch_stub_hit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;branch_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;branch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;uint32_t&lt;/span&gt; &lt;span class="n"&gt;target_idx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rb_execution_context_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ec&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;

  &lt;span class="cm"&gt;/* 生成实际的汇编代码(gen_block_version会重新调用gen_opt_aref)，gen_block_version在之前的gen_entry_point函数中也出现过 */&lt;/span&gt;
  &lt;span class="n"&gt;p_block&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gen_block_version&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target_ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ec&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;

  &lt;span class="n"&gt;dst_addr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p_block&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;start_addr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;dst_addr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="Basic Block Versioning"&gt;Basic Block Versioning&lt;/h3&gt;
&lt;p&gt;Basic Block Versioning 原理是把每段汇编代码缓存起来，形成用变量类型作为 key，汇编代码作为 value 的缓存。&lt;/p&gt;

&lt;p&gt;ruby，js 等动态类型的语言，同一个操作可以使用不同类型的变量。还是以字节码&lt;code&gt;opt_aref&lt;/code&gt;对应的 ruby 源码&lt;code&gt;[]&lt;/code&gt;为例子，可以作用在 Array, Hash, Proc/lambda等变量类型上, 例如如下代码&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;rec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;rec&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;rec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;rec&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;rec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Proc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;rec&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;所以 opt_aref 字节码对应的 c 源码有复杂的类型检查。Basic Block Versioning 的优化思路是，为当前变量类型生成特定的汇编代码，缓存下来。下次再遇到同样的字节码时，先检查变量类型，尝试从缓存中获取已有汇编代码内存地址；如果找不到缓存，再为新的变量类型，新增汇编代码，缓存下来。这样最终生成的汇编代码就只针对特定变量类型，易复用的同时更精简&lt;/p&gt;

&lt;p&gt;以上面的 get_branch_target 和 branch_stub_hit 函数为例，他们都先调用 &lt;a href="https://github.com/ruby/ruby/blob/fb4df44d1670e9d25aef6b235a7281199a177edb/yjit_core.c#L673" rel="nofollow" target="_blank" title=""&gt;find_block_version&lt;/a&gt; 尝试从缓存中获取汇编代码，精简后如下&lt;/p&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="nf"&gt;get_branch_target&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;blockid_t&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;ctx_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;branch_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;branch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kt"&gt;uint32_t&lt;/span&gt; &lt;span class="n"&gt;target_idx&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;

  &lt;span class="cm"&gt;/* 如果从缓存中找到对应的汇编代码,直接返回汇编代码内存地址, find_block_version 函数详情见下方 */&lt;/span&gt;
  &lt;span class="n"&gt;block_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;p_block&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;find_block_version&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// If the block already exists&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p_block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;

      &lt;span class="cm"&gt;/* 命中缓存，返回缓存汇编代码地址 */&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;p_block&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;start_addr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="cm"&gt;/* 未命中缓存，生成空壳汇编代码 */&lt;/span&gt;

  &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;

  &lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;stub_addr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cb_get_ptr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ocb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ocb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;write_pos&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="n"&gt;mov&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ocb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;C_ARG_REGS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;REG_EC&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="cm"&gt;/* 生成mov汇编代码，传REG_EC参数给branch_stub_hit */&lt;/span&gt;
  &lt;span class="n"&gt;mov&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ocb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;C_ARG_REGS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;imm_opnd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target_idx&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="cm"&gt;/* 生成mov汇编代码，传target_idx参数给branch_stub_hit */&lt;/span&gt;
  &lt;span class="n"&gt;mov&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ocb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;C_ARG_REGS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;const_ptr_opnd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;branch&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="cm"&gt;/* 生成mov汇编代码，传branch指针参数给branch_stub_hit */&lt;/span&gt;
  &lt;span class="n"&gt;call_ptr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ocb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;REG0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;branch_stub_hit&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="cm"&gt;/*  生成call汇编代码，调用branch_stub_hit函数, 详情见下方函数 */&lt;/span&gt;

  &lt;span class="n"&gt;jmp_rm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ocb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;RAX&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="cm"&gt;/* 生成jmp汇编代码，跳转到branch_stub_hit的返回值地址 */&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;stub_addr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/* 如果该函数被执行，说明可能对应的空壳汇编代码也真的被执行，需要生成实际的汇编代码 */&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="nf"&gt;branch_stub_hit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;branch_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;branch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;uint32_t&lt;/span&gt; &lt;span class="n"&gt;target_idx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rb_execution_context_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ec&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;

  &lt;span class="cm"&gt;/* 尝试从缓存中找汇编代码，find_block_version 函数详情见下方 */&lt;/span&gt;
  &lt;span class="n"&gt;block_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;p_block&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;find_block_version&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target_ctx&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="cm"&gt;/* 如果从缓存中没有找到汇编代码，生成新汇编代码 */&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;p_block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="cm"&gt;/* 生成实际的汇编代码(gen_block_version会重新调用gen_opt_aref)，gen_block_version在之前的gen_entry_point函数中也出现过 */&lt;/span&gt;
    &lt;span class="n"&gt;p_block&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gen_block_version&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target_ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ec&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;

  &lt;span class="n"&gt;dst_addr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p_block&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;start_addr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;dst_addr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;block_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="nf"&gt;find_block_version&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;blockid_t&lt;/span&gt; &lt;span class="n"&gt;blockid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;ctx_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cm"&gt;/* 获取该字节码的所有缓存 */&lt;/span&gt;
    &lt;span class="n"&gt;rb_yjit_block_array_t&lt;/span&gt; &lt;span class="n"&gt;versions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;yjit_get_version_array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;blockid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;iseq&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;blockid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;block_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;best_version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;best_diff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;INT_MAX&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="cm"&gt;/* 找出最匹配的缓存 */&lt;/span&gt;
    &lt;span class="n"&gt;rb_darray_for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;versions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;block_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rb_darray_get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;versions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;diff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctx_diff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="cm"&gt;/* 比较当前变量类型和缓存变量类型的差异, 详情见下方函数 */&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;diff&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;best_diff&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;best_version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="cm"&gt;/* 找出最匹配的缓存 */&lt;/span&gt;
            &lt;span class="n"&gt;best_diff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;diff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;

   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;best_version&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;
&lt;span class="nf"&gt;ctx_diff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;ctx_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;ctx_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;dst&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;

    &lt;span class="cm"&gt;/* 栈顶变量数量不同，放弃匹配 */&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dst&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;stack_size&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;stack_size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;INT_MAX&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="cm"&gt;/* sp偏移量不同，放弃匹配 */&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dst&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;sp_offset&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;sp_offset&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;INT_MAX&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Difference sum&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;diff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Check the type of self&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;self_diff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;type_diff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;self_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dst&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;self_type&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="cm"&gt;/* self 类型不同，放弃匹配 */&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self_diff&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;INT_MAX&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;INT_MAX&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="n"&gt;diff&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;self_diff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// For each local type we track&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;MAX_LOCAL_TYPES&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;   
        &lt;span class="n"&gt;val_type_t&lt;/span&gt; &lt;span class="n"&gt;t_src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;local_types&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="n"&gt;val_type_t&lt;/span&gt; &lt;span class="n"&gt;t_dst&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dst&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;local_types&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;temp_diff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;type_diff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t_src&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t_dst&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="cm"&gt;/* 本地变量类型不同，放弃匹配 */&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;temp_diff&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;INT_MAX&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;INT_MAX&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="n"&gt;diff&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;temp_diff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="cm"&gt;/* 临时推到栈顶的变量类型 */&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;stack_size&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;   
        &lt;span class="n"&gt;temp_type_mapping_t&lt;/span&gt; &lt;span class="n"&gt;m_src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctx_get_opnd_mapping&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;OPND_STACK&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="n"&gt;temp_type_mapping_t&lt;/span&gt; &lt;span class="n"&gt;m_dst&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctx_get_opnd_mapping&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dst&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;OPND_STACK&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

        &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;

        &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;temp_diff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;type_diff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m_src&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;m_dst&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="cm"&gt;/* 临时变量类型不同，放弃匹配 */&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;temp_diff&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;INT_MAX&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;INT_MAX&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="n"&gt;diff&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;temp_diff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;diff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;综上，Lazy Basic Block Versioning(LBBV) 就是利用【懒生成】和【变量类型缓存汇编】的 jit 优化方法。本文只浅析了大致原理，具体如何将汇编代码写入缓存，如何把不同缓存的汇编代码用 jmp 拼接在一起，如何处理分支 jmp 跳转，感兴趣的同学可自行深入浏览源码。&lt;/p&gt;
&lt;h2 id="缓存"&gt;缓存&lt;/h2&gt;
&lt;p&gt;根据&lt;a href="https://book.douban.com/subject/26344642/" rel="nofollow" target="_blank" title=""&gt;Computer System A Programmer's Perspective(深入理解计算机系统)&lt;/a&gt;，从硬件角度，本地硬盘是远端数据的缓存，内存是本地硬盘的缓存，CPU L1,L2,L3 是内存的缓存&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/zhuoerri/be4d6e17-29ce-4545-9c84-92780f379bb0.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;以我个人的知识经验：&lt;/p&gt;

&lt;p&gt;从操作系统的角度，内存是文件系统的缓存 (系统调用 fflush, fsync), TLB 是虚拟内存地址到物理内存地址的缓存。&lt;/p&gt;

&lt;p&gt;从 Web 服务器的角度，数据库索引是用于查找和排序的缓存，数据库视图是用于统计的缓存，数据库 MVCC 是事务中原数据的快照/缓存，redis 等又是数据库的缓存，cdn 又是 web 服务器的缓存，HTTP 的 ETag, last-modified 又是浏览器到服务器间的缓存。&lt;/p&gt;

&lt;p&gt;从 Ruby 语言的角度，之前同样是 Shopify 开发的 bootsnap, 用覆盖 Kernel#require 的方式实现了 Gem 文件相对文件路径到绝对文件路径的缓存; 用 RubyVM::InstructionSequence.load_iseq 实现了 Ruby 源码到字节码的缓存。而本文的 YJIT 则实现了字节码到汇编代码的缓存。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“计算机科学只存在两个难题：缓存失效和命名。” ——Phil KarIton&lt;/p&gt;
&lt;/blockquote&gt;</description>
      <author>zhuoerri</author>
      <pubDate>Tue, 18 Jan 2022 13:13:51 +0800</pubDate>
      <link>https://ruby-china.org/topics/42087</link>
      <guid>https://ruby-china.org/topics/42087</guid>
    </item>
    <item>
      <title>Ruby require 与动态链接库</title>
      <description>&lt;h2 id="前言"&gt;前言&lt;/h2&gt;
&lt;p&gt;本人水平有限，如有错误，欢迎指正与补充。&lt;/p&gt;

&lt;p&gt;本文会先介绍 require 的基本使用，然后介绍动态链接库的基本知识。&lt;/p&gt;

&lt;p&gt;最后深入 ruby 源码，查找对 dlopen, dlsym 的调用&lt;/p&gt;
&lt;h2 id="require"&gt;require&lt;/h2&gt;
&lt;p&gt;ruby 里加载第三方 gem 的方式，通常是用 require 加载其 lib/ 文件夹下的某 .rb 文件。&lt;/p&gt;

&lt;p&gt;但除了.rb 文件，gem 内也可以包含 c 语言写的源码，源码入口函数名往往是例如 &lt;code&gt;Init_xxx&lt;/code&gt; 的格式。当我们用 gem install 安装 gem 的过程中，c 源码被编译成，例如名叫 &lt;code&gt;xxx.so&lt;/code&gt; 的动态链接库。&lt;/p&gt;

&lt;p&gt;最后 require 时，我们可以像加载 .rb 文件一样。用 &lt;code&gt;require 'xxx.so'&lt;/code&gt; 加载动态链接库内的&lt;code&gt;Init_xxx&lt;/code&gt; 函数。&lt;/p&gt;
&lt;h2 id="动态链接库"&gt;动态链接库&lt;/h2&gt;
&lt;p&gt;对于不了解链接的同学，推荐 &lt;a href="https://book.douban.com/subject/1230413/" rel="nofollow" target="_blank" title=""&gt;《深入理解计算机系统》&lt;/a&gt; 的第七章链接部分。或者 &lt;a href="https://book.douban.com/subject/3652388/" rel="nofollow" target="_blank" title=""&gt;《程序员的自我修养》&lt;/a&gt; 也行。简单来说，链接就是把不同的 c 语言库和在一起，方便相互之间调用各自的函数与变量。&lt;/p&gt;

&lt;p&gt;这个和的过程，可以是在编译时完成，称作静态链接。代价是最终的可执行文件较大。&lt;/p&gt;

&lt;p&gt;这个和的过程，也可以推迟到程序启动时才完成，称作动态链接。优点是编译时，可执行文件只需记下自己依赖的库（linux 下可用 ldd 查看程序依赖哪些库），不用实际链接。等到启动时再实际链接，除了节省可执行文件的磁盘空间，也因为多个可执行文件共享同一个库，节省了内存。&lt;/p&gt;

&lt;p&gt;这个和的过程，甚至可以在程序运行时进行。方法是调用 &lt;a href="https://man7.org/linux/man-pages/man3/dlopen.3.html" rel="nofollow" target="_blank" title=""&gt;dlopen&lt;/a&gt; 函数加载动态链接库，再用 &lt;a href="https://man7.org/linux/man-pages/man3/dlsym.3.html" rel="nofollow" target="_blank" title=""&gt;dlsym&lt;/a&gt; 获取动态链接库内的函数引用。&lt;/p&gt;

&lt;p&gt;综上不难猜到，ruby require 动态链接库的过程，就是 调用 &lt;code&gt;dlopen&lt;/code&gt; 加载动态链接库，再用 &lt;code&gt;dlsym&lt;/code&gt; 获取 &lt;code&gt;Init_xxx&lt;/code&gt; 函数指针并调用的过程。&lt;/p&gt;
&lt;h2 id="ruby 源码"&gt;ruby 源码&lt;/h2&gt;
&lt;p&gt;require 对应的 c 函数是在 load.c 中的 &lt;a href="https://github.com/ruby/ruby/blob/80c180df769016d58663218de2687613841864f3/load.c#L1357" rel="nofollow" target="_blank" title=""&gt;rb_f_require&lt;/a&gt; , 精简省略后，主要的调用栈如下&lt;/p&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* load.c */&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt;
&lt;span class="nf"&gt;Init_load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;rb_define_global_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"require"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rb_f_require&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;VALUE&lt;/span&gt;
&lt;span class="nf"&gt;rb_f_require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="n"&gt;fname&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="cm"&gt;/* 调用 rb_require_string */&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;rb_require_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fname&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;VALUE&lt;/span&gt;
&lt;span class="nf"&gt;rb_require_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="n"&gt;fname&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;

  &lt;span class="cm"&gt;/* 调用 require_internal */&lt;/span&gt;
  &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;require_internal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;
&lt;span class="nf"&gt;require_internal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rb_execution_context_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="n"&gt;fname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;exception&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="cm"&gt;/*  省略 */&lt;/span&gt;  

  &lt;span class="cm"&gt;/* 调用 rb_vm_call_cfuncs, 其中 load_ext 参数是回调函数，也是实际被调用的函数。path 是动态链接库的文件地址，其他的参数可以忽略 */&lt;/span&gt;
  &lt;span class="n"&gt;handle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;long&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;rb_vm_call_cfunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rb_vm_top_self&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;load_ext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                            &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;VM_BLOCK_HANDLER_NONE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;VALUE&lt;/span&gt;
&lt;span class="nf"&gt;load_ext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="cm"&gt;/* 调用 dln_load,  RSTRING_PTR(path)参数是把path转成字符指针类型 */&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;VALUE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;dln_load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RSTRING_PTR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;最终实际调用&lt;code&gt;dlopen&lt;/code&gt;的函数在 dln.c 的 &lt;a href="https://github.com/ruby/ruby/blob/80c180df769016d58663218de2687613841864f3/dln.c#L1264" rel="nofollow" target="_blank" title=""&gt;dln_load&lt;/a&gt; 中，其中有针对不同操作系统的宏判断，这里只精简显示 linux 相关的 &lt;code&gt;dlopen&lt;/code&gt;&lt;/p&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* dln.c */&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="nf"&gt;dln_load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="cm"&gt;/* 省略 */&lt;/span&gt;

     &lt;span class="cm"&gt;/* buf存储动态链接库的入口函数名，即例子中的 Init_xxx */&lt;/span&gt;
    &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;init_funcname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="cm"&gt;/* 调用dlopen , 获取动态链接库对应的 handle指针 */&lt;/span&gt;
    &lt;span class="n"&gt;handle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;dlopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;RTLD_LAZY&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;RTLD_GLOBAL&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="cm"&gt;/* 调用dlsym, 获取 buf即例子Init_xxx 的函数指针*/&lt;/span&gt;
    &lt;span class="n"&gt;init_fct&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)())(&lt;/span&gt;&lt;span class="n"&gt;VALUE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;dlsym&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="cm"&gt;/* 用函数指针调用 例子Init_xxx 函数 */&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;init_fct&lt;/span&gt;&lt;span class="p"&gt;)();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;</description>
      <author>zhuoerri</author>
      <pubDate>Sun, 08 Aug 2021 07:56:42 +0800</pubDate>
      <link>https://ruby-china.org/topics/41560</link>
      <guid>https://ruby-china.org/topics/41560</guid>
    </item>
    <item>
      <title>Rails 路由 Journey 与 有限状态自动机</title>
      <description>&lt;h2 id="前言"&gt;前言&lt;/h2&gt;
&lt;p&gt;本人水平有限，如有错误，欢迎指正与补充。&lt;/p&gt;

&lt;p&gt;Rails 路由的作用是根据 config/routes.rb 的规则，生成有限状态自动机来匹配 http 请求的路径，找到对应的 controller 与 action。然后将 rack 的请求转给 controller 的 action&lt;/p&gt;

&lt;p&gt;本文主要介绍有限状态自动机的部分，Mapper 以及 RouteSet 只会一笔带过，论坛里早已有过分析 Mapper 和 RouteSet 等源码的帖子，感兴趣的同学可阅读下面的链接。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ruby-china.org/topics/22726" rel="nofollow" target="_blank"&gt;https://ruby-china.org/topics/22726&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ruby-china.org/topics/30916" rel="nofollow" target="_blank"&gt;https://ruby-china.org/topics/30916&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ruby-china.org/topics/34110" rel="nofollow" target="_blank"&gt;https://ruby-china.org/topics/34110&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ruby-china.org/topics/30929" rel="nofollow" target="_blank"&gt;https://ruby-china.org/topics/30929&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="Mapper"&gt;Mapper&lt;/h2&gt;
&lt;p&gt;config/routes.rb 里各种方法的源码主要在 actionpack/lib/action_dispatch/routing/mapper.rb 里，例如最常用的 resources 方法就定义在 &lt;a href="https://github.com/rails/rails/blob/14ff158e73f17d2cb76f89f0d8c84a556f7589d9/actionpack/lib/action_dispatch/routing/mapper.rb#L1477" rel="nofollow" target="_blank" title=""&gt;mapper.rb&lt;/a&gt;  里。&lt;/p&gt;
&lt;h2 id="Racc与抽象语法树"&gt;Racc 与抽象语法树&lt;/h2&gt;
&lt;p&gt;Mapper 里识别了路径后，再用 &lt;a href="https://github.com/ruby/racc" rel="nofollow" target="_blank" title=""&gt;Racc&lt;/a&gt; 根据 &lt;a href="https://github.com/rails/rails/blob/14ff158e73f17d2cb76f89f0d8c84a556f7589d9/actionpack/lib/action_dispatch/journey/parser.y" rel="nofollow" target="_blank" title=""&gt;actionpack/lib/action_dispatch/journey/parser.y&lt;/a&gt; 自动生成的 &lt;a href="https://github.com/rails/rails/blob/14ff158e73f17d2cb76f89f0d8c84a556f7589d9/actionpack/lib/action_dispatch/journey/parser.rb" rel="nofollow" target="_blank" title=""&gt;actionpack/lib/action_dispatch/journey/parser.rb&lt;/a&gt;。来生成以 &lt;code&gt;Journey::Nodes::Node&lt;/code&gt; 为节点的抽象语法树。例如如下代码&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;ActionDispatch&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Journey&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/users/new"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;#&amp;lt;ActionDispatch::Journey::Nodes::Cat:0x0055a9bd16a0a0&lt;/span&gt;
 &lt;span class="vi"&gt;@left&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="c1"&gt;#&amp;lt;ActionDispatch::Journey::Nodes::Slash:0x0055a9bd16a4d8 @left="/", @memo=nil&amp;gt;,&lt;/span&gt;
 &lt;span class="vi"&gt;@memo&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="vi"&gt;@right&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="c1"&gt;#&amp;lt;ActionDispatch::Journey::Nodes::Cat:0x0055a9bd16a0f0&lt;/span&gt;
   &lt;span class="vi"&gt;@left&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="c1"&gt;#&amp;lt;ActionDispatch::Journey::Nodes::Literal:0x0055a9bd16a3c0 @left="users", @memo=nil&amp;gt;,&lt;/span&gt;
   &lt;span class="vi"&gt;@memo&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="vi"&gt;@right&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="c1"&gt;#&amp;lt;ActionDispatch::Journey::Nodes::Cat:0x0055a9bd16a140&lt;/span&gt;
     &lt;span class="vi"&gt;@left&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="c1"&gt;#&amp;lt;ActionDispatch::Journey::Nodes::Slash:0x0055a9bd16a2f8 @left="/", @memo=nil&amp;gt;,&lt;/span&gt;
     &lt;span class="vi"&gt;@memo&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="vi"&gt;@right&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="c1"&gt;#&amp;lt;ActionDispatch::Journey::Nodes::Literal:0x0055a9bd16a1e0 @left="new", @memo=nil&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;整理一下返回结果，画成图如下&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/zhuoerri/41481195-c430-4e71-b752-f39841be7a52.png!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h2 id="Journey"&gt;Journey&lt;/h2&gt;&lt;h2 id="语法树"&gt;语法树&lt;/h2&gt;
&lt;p&gt;Journey 把每个路由规则的小语法树，拼成一个大语法树，以 Node::Or 作为 root 节点，例如如图。
&lt;img src="https://l.ruby-china.com/photo/zhuoerri/321fe2ad-4820-4b92-a7bd-c7d859b903d8.png!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h2 id="有限状态自动机"&gt;有限状态自动机&lt;/h2&gt;
&lt;p&gt;以上图为例，当 http 请求的路径是&lt;code&gt;/users/2&lt;/code&gt; 时。Journey 依次读到了的是 &lt;code&gt;/&lt;/code&gt;, &lt;code&gt;users&lt;/code&gt;, &lt;code&gt;/&lt;/code&gt;, &lt;code&gt;2&lt;/code&gt;。当读到&lt;code&gt;/&lt;/code&gt;, &lt;code&gt;users&lt;/code&gt;  的这两个前缀时，从大语法树的 root 节点出发，每个子语法树 &lt;code&gt;/users/new&lt;/code&gt;, &lt;code&gt;/users&lt;/code&gt;, &lt;code&gt;/users/:id&lt;/code&gt; 都匹配，这时就不知道该应该走哪个子节点来匹配。&lt;/p&gt;

&lt;p&gt;这种处于某个节点状态，根据输入的数据能转移到多个子节点，不确定应该转到哪个节点才能匹配的图，就是 &lt;a href="https://zh.wikipedia.org/wiki/%E9%9D%9E%E7%A1%AE%E5%AE%9A%E6%9C%89%E9%99%90%E7%8A%B6%E6%80%81%E8%87%AA%E5%8A%A8%E6%9C%BA" rel="nofollow" target="_blank" title=""&gt;不确定的有限状态自动机 nfa&lt;/a&gt;. &lt;/p&gt;
&lt;h2 id="nfa转dfa"&gt;nfa 转 dfa&lt;/h2&gt;
&lt;p&gt;不确定有限状态自动机是可以转为确定的有限状态自动机 dfa(每个输入能确定跳转到唯一的目标节点)，原理就是把多个不确定有限状态自动机的节点，组成一个集和，作为一个新状态。这个新状态作为确定的有限状态自动机里的节点状态。例如下图&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/zhuoerri/cb7188f8-f701-4eeb-b345-6c2ebaea814d.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;左边是不确定的有限状态自动机，右边是对应的确定的优先状态自动机。黄色节点为接受状态节点。
左右都可以识别 12，13，124，134 这四个字符串。但左边的 a 节点读取到 1 时，不能确定跳转到 b，还是 c。而右边节点的 A，读取到 1 时，直接确定跳转到 B。所以右边的 B 对应左边的 [b, c]。右边其他节点同理。&lt;/p&gt;
&lt;h2 id="Journey::GTG::TransitionTable"&gt;Journey::GTG::TransitionTable&lt;/h2&gt;
&lt;p&gt;rails 里把 整个 Nodes::Node 的 nfa 转为 TransitionTable 的 dfa，源码在  &lt;a href="https://github.com/rails/rails/blob/302fdb7677b95ac1c428bd5c545252bc52ac2313/actionpack/lib/action_dispatch/journey/gtg/builder.rb#L19" rel="nofollow" target="_blank" title=""&gt;Journey::GTG::Builder.transition_table&lt;/a&gt; , 省略精简后，主要逻辑如下&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;transition_table&lt;/span&gt;
  &lt;span class="c1"&gt;# dtrans 用来存储最终的 dfa&lt;/span&gt;
  &lt;span class="n"&gt;dtrans&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;TransitionTable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;

  &lt;span class="c1"&gt;# 给新生成的dfa节点赋值一个id&lt;/span&gt;
  &lt;span class="n"&gt;state_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Hash&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;length&lt;/span&gt; &lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;compare_by_identity&lt;/span&gt;

  &lt;span class="c1"&gt;# root 是 nfa的根节点，dstates里存储了从根节点出发，能转移到的所有节点组成的出发节点数组&lt;/span&gt;
  &lt;span class="n"&gt;dstates&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;firstpos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;

  &lt;span class="k"&gt;until&lt;/span&gt; &lt;span class="n"&gt;dstates&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;empty?&lt;/span&gt;
    &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dstates&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shift&lt;/span&gt;

    &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;group_by&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;sym&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ps&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="c1"&gt;# u是一个数组，对应原nfa中所有可能跳转到的目标节点数组&lt;/span&gt;
      &lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ps&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flat_map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="vi"&gt;@followpos&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;uniq&lt;/span&gt;
      &lt;span class="k"&gt;next&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;empty?&lt;/span&gt;

      &lt;span class="c1"&gt;# from是新建的dfa里的出发节点，对应原nfa里s出发节点数组&lt;/span&gt;
      &lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;state_id&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

      &lt;span class="c1"&gt;# 如果所有nfa中的目标节点数组中节点都是接受节点&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all?&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;pos&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;pos&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;DUMMY_END_NODE&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="c1"&gt;# 创建一个新 dfa 节点&lt;/span&gt;
        &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;state_id&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="c1"&gt;# 写入dfa中，从from节点，读取到sym后，进入to节点&lt;/span&gt;
        &lt;span class="n"&gt;dtrans&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;from&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sym&lt;/span&gt;
        &lt;span class="c1"&gt;# 把to节点标记为接受节点&lt;/span&gt;
        &lt;span class="n"&gt;dtrans&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_accepting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="c1"&gt;# 根据nfa的u节点数组，读取或新建对应的dfa节点to&lt;/span&gt;
        &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;state_id&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="c1"&gt;# 写入dfa中，从from节点，读取到sym后，进入to节点&lt;/span&gt;
        &lt;span class="n"&gt;dtrans&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;from&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sym&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;DUMMY_END_NODE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="c1"&gt;# 如果u节点数组中有接受节点，把to节点标记为接受节点&lt;/span&gt;
          &lt;span class="n"&gt;dtrans&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_accepting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

      &lt;span class="c1"&gt;# 把u节点数组作为出发节点，继续循环&lt;/span&gt;
      &lt;span class="n"&gt;dstates&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="处理rack请求"&gt;处理 rack 请求&lt;/h2&gt;
&lt;p&gt;router 收到 rack 请求后，会尝试用上面的 TransitionTable 来匹配 req.path_info。匹配的源码主要在 &lt;a href="https://github.com/rails/rails/blob/302fdb7677b95ac1c428bd5c545252bc52ac2313/actionpack/lib/action_dispatch/journey/gtg/simulator.rb#L25" rel="nofollow" target="_blank" title=""&gt;Journey::GTG::Simulator#memos&lt;/a&gt;&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;memos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# input 是要匹配的路径，例如 /users/new&lt;/span&gt;
    &lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;StringScanner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# INITIAL_STATE = [ [0, nil] ].&lt;/span&gt;
    &lt;span class="c1"&gt;# dfa 的初始节点状态，即0 &lt;/span&gt;
    &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;INITIAL_STATE&lt;/span&gt;

    &lt;span class="n"&gt;start_index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;sym&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;%r([/.?]|[^/.?]+)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;end_index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;start_index&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;sym&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;length&lt;/span&gt;

     &lt;span class="c1"&gt;# tt是TransitionTable&lt;/span&gt;
     &lt;span class="c1"&gt;# 从state节点出发，读取到 string[start_index, end_index] 后，跳转的目标节点又赋值给state&lt;/span&gt;
      &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;move&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;start_index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end_index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="n"&gt;start_index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;end_index&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="c1"&gt;# 如果最终有可接受节点，返回节点信息。否则 yield&lt;/span&gt;
    &lt;span class="n"&gt;acceptance_states&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each_with_object&lt;/span&gt;&lt;span class="p"&gt;([])&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;s_d&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;memos&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;idx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s_d&lt;/span&gt;
      &lt;span class="n"&gt;memos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;concat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;memo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;nil?&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;tt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accepting?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;acceptance_states&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;empty?&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;acceptance_states&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;之后 router 根据节点里存的 controller 和 aciton 信息，把 rack 请求转给对应的 controller&lt;/p&gt;
&lt;h2 id="总结"&gt;总结&lt;/h2&gt;
&lt;p&gt;本文只讲了有限状态机，具体的判断逻辑其实更复杂，例如 /users/:id 这种 :id 参数名，dfa 匹配时需要考虑正则表达式。但本文都省略了，感兴趣的同学可以自行深入浏览源码。&lt;/p&gt;

&lt;p&gt;最后推荐一本书的前半部分（因为我只读了前半部分）&lt;a href="https://book.douban.com/subject/26148763/" rel="nofollow" target="_blank" title=""&gt;《计算的本质》&lt;/a&gt;，前半部分用 ruby 语言实现了简单的词法分析器与语法分析器。虽然从编译原理的角度看不够专业，但特别适小白合作为看 Rails 路由 Journey 前的预备资料。&lt;/p&gt;</description>
      <author>zhuoerri</author>
      <pubDate>Thu, 04 Mar 2021 13:01:04 +0800</pubDate>
      <link>https://ruby-china.org/topics/40988</link>
      <guid>https://ruby-china.org/topics/40988</guid>
    </item>
    <item>
      <title>head 请求绕过 Github 的 OAuth</title>
      <description>&lt;p&gt;看阮一峰老师的科技爱好者周刊，提到的&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.teddykatz.com/2019/11/05/github-oauth-bypass.html" rel="nofollow" target="_blank"&gt;https://blog.teddykatz.com/2019/11/05/github-oauth-bypass.html&lt;/a&gt;&lt;/p&gt;</description>
      <author>zhuoerri</author>
      <pubDate>Fri, 20 Dec 2019 19:13:57 +0800</pubDate>
      <link>https://ruby-china.org/topics/39360</link>
      <guid>https://ruby-china.org/topics/39360</guid>
    </item>
  </channel>
</rss>
