Erlang的原子(atom)在匹配中有着重要作用,它兼顾了可读性和运行效率。 通过atom,可以实现很多灵活高效的应用。

atom可以看作是给字符串生成了一个ID,内部使用的是ID值,必要时可以取出它的内容(字符串),例如用于打印输出。

atom 的 Eterm 除去 6 位的标签之外剩下的部分,就是 atom 在 Erlang 虚拟机中的索引,也就是一个整数值。在 Erlang 中,有关 atom 比较的操作只需要比较两个索引值即可,就是整数操作,因此非常高效。atom 本身是一个字符串,那么 atom 的索引是怎样对应上具体的字符串的呢?也就是需要实现字符串和索引值之间的互相映射,字符串和索引值都必须唯一,这显然需要使用散列表。Erlang 虚拟机内实现了一套通用的索引和散列表机制,atom 表就是这个机制的一个客户。下图是这套机制中关键数据结构之间的关系。

图中左侧是散列表部分,右侧是索引部分。先看左侧。这个散列表采用的是标准的数据结构教科书上的实现。查找的时候:通过散列函数计算被散列对象的散列值,然后对散列表的长度取模,得到图中左侧指针数组的索引,接下来运气好的话能直接得到查找的对象(封装在 HashBucket 中),运气不好的话可能查不到,或者发生碰撞进行线性搜索(例如图中通过散列值得到索引 2 的时候就发生了碰撞,需要线性搜索 HashBucket 中匹配的 hvalue)。插入的时候:同样是先计算散列值得到索引,然后看对应的指针是否已经有对象了,如果没有,则直接加入,如果有的话,则插入队列头部。散列表在扩容的时候,会选择下一个合适的大小(erts/emulator/beam/hash.c 文件中的 h_size_table 数组列出了散列表大小增长的序列,数组里面都是素数,但是基本上符合倍增的关系),把老表复制到新表,然后删除老表。当然,增长是有限制的,散列表大小不能超出 h_size_table 数组中指定的最大值。

图中右侧是索引部分。索引表实际上是指向被索引对象的指针的数组,被索引对象的索引值就是对应指针在数组中的自然顺序。由于事先无法确定具体的索引数目,所以索引表的大小是动态增长的,增长单位为一个索引块的大小,每个索引块中有固定数目的指针(例如 1024 个)。散列表中每插入一个新的对象的时候,设置索引表中最小的那个可用索引。如果新的索引超出了索引块的边界,那么分配一个新的索引块,并且更新索引块表中的指针。同样,索引表的增长也是有限度的,索引块表的指针用完了就不能再增长了。索引块表的长度是在创建索引表的时候设置的,所以理论上可以很大,不超出内存限制即可,但是实际中还要考虑散列表的大小,这两者是相互制约的。

描述了散列和索引的数据结构和实现之后,我们回到散列索引的客户——atom。由于散列和索引是通用的,所以散列表指向的对象是 HashBucket 数据结构,而索引表指向的是 IndexSlot 数据结构。为了将散列和索引结合起来,这两个数据结构是重叠的,HashBucket 在 IndexSlot 头部。具体客户在使用的时候,要把 IndexSlot 放在自己的头部,这样就把具体的对象和散列索引结合起来了(就好像原始的面向对象的实现)。 从图中可以看出,我们的 atom 数据结构除了上述结构之外,还包含了具体的字符串指针、长度以及编码信息。将这些信息串起来之后,我们就可以高效地在常量时间内查询 atom 是否已经存在,已经存在的 atom 的索引值是什么,某个索引值对应的 atom 是什么以及插入新的 atom。

另一个问题,向散列表插入元素的时候,散列表要负责分配对象的内存,而散列表是通用的,那么散列表怎么知道分配例如 atom 呢?解决方法是散列表中元素的分配、比较、释放以及散列值计算的操作都通过回调函数的方式提供给散列表。这里 atom 使用的散列函数是经典的 hashpjw 散列算法,这个算法是字符串散列常用的算法。

在一个 Erlang 节点内,atom 表是全局共享的,因此多个线程对 atom 表的访问是通过读写锁保护的。对 atom 表的操作绝大部分都是读操作,只有真正插入新的 atom 时的操作才是写操作,插入新 atom 的情况一般不频繁,而且也很少有多个线程争抢着插入新 atom 的情况,大部分情况都是试图插入 atom 但是发现其实已经存在了,因此 atom 表使用的读写锁是针对读操作优化的读写锁。使用针对读操作优化的读写锁时读锁的开销非常小,即使是在大量线程争用的情况下。

Erlang 中的 atom 表是不进行垃圾回收的,毕竟在程序员不滥用 atom 的情况下,atom 数目可以控制在合理范围内。而且跟踪每一个 atom 的引用状况会产生很大的开销。所以不要滥用 atom,把 atom 表塞满是把 Erlang 虚拟机 crash 掉的一种方法。目前默认的 atom 数目限制是 1048576(1024×1024),通过虚拟机的 +t 参数可以设置。

atom的实现文件位于erts/emulator/beam/atom.h atom.c  index.h index.c hash.h hash.c

本文内容转自:http://www.cnblogs.com/zhengsyao/p/3424539.html

Erlang中atom的实现的更多相关文章

  1. Erlang中atom的实现[转]

    转自: http://www.cnblogs.com/zhengsyao/p/3424539.html 在 Erlang 中,使用 atom 既方便又高效,我们就来看看 atom 是怎么实现的.ato ...

  2. erlang中的原子(atom)内部实现[转]

    转自: http://www.kongqingquan.com/archives/208#more-208 Erlang中的atom由通用hash表实现,虚拟机中atom最终的用数值表示,对应表中的下 ...

  3. Erlang中一些错误或者异常的标识

    erlang中错误大体分为四种: 1. 编译错误    2. 逻辑错误    3. 运行时错误    4. 用户代码生成的错误 编译错误,主要是编译器检测出的代码语法错误 逻辑错误,是指程序没有完成预 ...

  4. [erlang 001] erlang中的错误及异常处理

    一. erlang中的错误 1. 分类 1) 编译错误:主要是编译器检测出的代码语法错误: 2) 逻辑错误:是指程序没有完成预期的工作,属于开发人员的问题: 3) 运行时错误:是指erlang运行时抛 ...

  5. Socket的UDP协议在erlang中的实现

    现在我们看看UDP协议(User Datagram Protocol,用户数据报协议).使用UDP,互联网上的机器之间可以互相发送小段的数据,叫做数据报.UDP数据报是不可靠的,这意味着如果客户端发送 ...

  6. erlang中字符编码转换(转)

    转自:http://www.thinksaas.cn/group/topic/244329/ 功能说明: erlang中对各种语言的编码支持不足,此代码是使用erlang驱动了著名的iconv编码库来 ...

  7. Erlang中日志管理

    http://blog.sina.com.cn/s/blog_96b8a1540101317g.html 一.基本概念 在Erlang中,通过两个概念管理错误事情:事件管理器(event manage ...

  8. erlang中变量作用域

    http://erlangdisplay.iteye.com/blog/315452 _开头(包括_)在erlang可以是表明,这个变量可以存任意东西,就是我们常说的全匹配,_A一般来说就是表明这个东 ...

  9. Erlang中频繁发送远程消息要注意的问题

    http://avindev.iteye.com/blog/76373 注:这篇文章可能会有争议,欢迎提出意见 在Erlang中,如果要实现两个远程节点之间的通信,就需要通过网络来实现,对于消息发送, ...

随机推荐

  1. 一个VLAN配置的实际例子

    背景很简单,和一般的eth-switch通过VLAN做成路由的方式一样.     首先看一种硬件效率较高的方法: Port1~4作为access口,同时在硬件上作为用户模式,即从PC发往这些端口的数据 ...

  2. memcpy实现

    typedef unsigned int size_t; void * my_memcpy ( void *dest, const void *src, size_t num ) { void* re ...

  3. python的内存回收机制即gc模块讲解

    最后容易造成内存问题的通常就是全局单例.全局缓存.长期存活的对象 引用计数(主要), 标记清除, 分代收集(辅助) 引用计数为0则会被gc回收.标记删除可以解决循环引用的问题.分代:0代--年轻代:1 ...

  4. 【BZOJ】【3240】【NOI2013】矩阵游戏

    十进制快速幂+矩阵乘法+常数优化 听说这题还可以强行算出来递推式……然后乘乘除除算出来…… 然而蒟蒻选择了一个比较暴力的做法= = 我们发现这个递推的过程是线性的,所以可以用矩阵乘法来表示,$x=a* ...

  5. Informatica 常用组件Filter之二 过滤条件

    过滤条件可以使用转换语言输入.过滤条件是返回 TRUE 或 FALSE 的表达式.例如,如果您要过滤出员工薪水低于 $30,000 的行,可输入以下条件: SALARY > 30000 可以使用 ...

  6. 附4 springboot源码解析-run()

    public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); / ...

  7. 第十三章 springboot + lombok

    lombok作用:消除模板代码. getter.setter.构造器.toString().equals() 便捷的生成比较复杂的代码,例如一个POJO要转化成构建器模式的形式,只需要一个注解. 注意 ...

  8. Android中XML解析-Dom解析

    Android中需要解析服务器端传过来的数据,由于XML是与平台无关的特性,被广泛运用于数据通信中,有的时候需要解析xml数据,格式有三种方式,分别是DOM.SAX以及PULL三种方式,本文就简单以D ...

  9. Jquery 获取第一个子元素

    <ul>  <li>John</li>  <li>Karl</li>  <li>Brandon</li></u ...

  10. 11个JavaScript颜色选择器插件

    几年前,很难找到一个合适的颜色选择器.正好看到很多不错的JavaScript颜色选择器插件,故而把这些编译汇总.在本文,Web设计师和开发人员 Kevin Liew 选取了11个相应插件,有些会比较复 ...