我的项目已经超过了最大的1M原子,我们已经达到极限,但我需要对人们提交的关于list_to_atom及其朋友的代码应用一些理智.我想从获得所有已注册原子的列表开始,这样我就可以看到最大的罪犯在哪里.有没有办法做到这一点.我必须对我如何做到这一点有创意,所以我最终不会尝试在实时控制台中转储1-2M行.
谢谢.
leg*_*cia 36
您可以使用外部术语格式的未记录功能来获取所有原子.
TL; DR:将以下行粘贴到正在运行的节点的Erlang shell中.继续阅读以获得解释和非简洁版本的代码.
(fun F(N)->try binary_to_term(<<131,75,N:24>>) of A->[A]++F(N+1) catch error:badarg->[]end end)(0).
Run Code Online (Sandbox Code Playgroud)
Ivar Vong的 Elixir版本:
for i <- 0..:erlang.system_info(:atom_count)-1, do: :erlang.binary_to_term(<<131,75,i::24>>)
Run Code Online (Sandbox Code Playgroud)
以外部术语格式编码的Erlang术语以字节131开始,然后是标识类型的字节,然后是实际数据.我发现EEP-43提到了所有可能的类型,包括ATOM_INTERNAL_REF3类型字节75,这在外部术语格式的官方文档中没有提到.
因为ATOM_INTERNAL_REF3,数据是原子表的索引,编码为24位整数.我们可以轻松创建这样的二进制文件<<131,75,N:24>>
例如,在我的Erlang VM中,false似乎是原子表中的第0个原子:
> binary_to_term(<<131,75,0:24>>).
false
Run Code Online (Sandbox Code Playgroud)
找到原子表*中当前原子的数量没有简单的方法,但我们可以继续增加数字,直到我们得到badarg错误.
所以这个小模块给你一个所有原子的列表:
-module(all_atoms).
-export([all_atoms/0]).
atom_by_number(N) ->
binary_to_term(<<131,75,N:24>>).
all_atoms() ->
atoms_starting_at(0).
atoms_starting_at(N) ->
try atom_by_number(N) of
Atom ->
[Atom] ++ atoms_starting_at(N + 1)
catch
error:badarg ->
[]
end.
Run Code Online (Sandbox Code Playgroud)
输出如下:
> all_atoms:all_atoms().
[false,true,'_',nonode@nohost,'$end_of_table','','fun',
infinity,timeout,normal,call,return,throw,error,exit,
undefined,nocatch,undefined_function,undefined_lambda,
'DOWN','UP','EXIT',aborted,abs_path,absoluteURI,ac,accessor,
active,all|...]
> length(v(-1)).
9821
Run Code Online (Sandbox Code Playgroud)
*在Erlang/OTP 20.0中,您可以致电erlang:system_info(atom_count):
> length(all_atoms:all_atoms()) == erlang:system_info(atom_count).
true
Run Code Online (Sandbox Code Playgroud)
我不确定是否有办法在实时系统上执行此操作,但如果您可以在测试环境中运行它,则应该能够通过崩溃转储获取列表.原子表接近崩溃转储格式的末尾.您可以通过erlang:halt/1创建故障转储,但这会导致整个运行时系统崩溃.