我的项目已经超过了最大 1M 个原子,我们已经提高了限制,但是我需要对人们提交的关于 list_to_atom 及其朋友的代码进行一些理智的处理。我想首先获取所有已注册原子的列表,这样我就可以看到最大的违规者在哪里。有没有办法做到这一点。我必须对如何做到这一点有创意,所以我最终不会试图在实时控制台中转储 1-2M 行。
4 回答
您可以使用外部术语格式的未记录功能来获取所有原子。
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).
Ivar Vong 的Elixir 版本:
for i <- 0..:erlang.system_info(:atom_count)-1, do: :erlang.binary_to_term(<<131,75,i::24>>)
以外部术语格式编码的 Erlang 术语以字节 131 开头,然后是标识类型的字节,然后是实际数据。我发现EEP-43提到了所有可能的类型,包括类型 byte 75,在外部术语格式的官方文档中ATOM_INTERNAL_REF3
没有提到。
对于ATOM_INTERNAL_REF3
,数据是原子表的索引,编码为 24 位整数。我们可以轻松创建这样的二进制文件:<<131,75,N:24>>
例如,在我的 Erlang VM 中,false
似乎是原子表中的第零个原子:
> binary_to_term(<<131,75,0:24>>).
false
没有简单的方法可以找到当前原子表*中的原子数量,但我们可以不断增加数量,直到出现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.
输出如下所示:
> 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
*在 Erlang/OTP 20.0 中,您可以调用erlang:system_info(atom_count)
:
> length(all_atoms:all_atoms()) == erlang:system_info(atom_count).
true
我不确定是否有办法在实时系统上执行此操作,但如果您可以在测试环境中运行它,您应该能够通过故障转储获取列表。原子表接近故障转储格式的末尾。您可以通过erlang:halt/1创建故障转储,但这会导致整个运行时系统崩溃。
我敢说,如果你使用超过 100 万个原子,那么你做错了什么。一旦应用程序运行,Atoms 就应该是静态的,或者至少以一些小的数字为上限,对于中型应用程序来说是 3000 左右。
当敌人可以在你的虚拟机中生成原子时要非常小心。特别是像 list_to_atom/1 这样的调用有点危险。
已编辑(错误答案..)
您可以调整原子数+t
http://www.erlang.org/doc/efficiency_guide/advanced.html
..但我知道很少有必要的用例。
您可以使用以下命令跟踪原子统计信息erlang:memory()