在Erlang应用程序中查找内存泄漏

作为用 Erlang 编写的网络模拟器的一部分,我通常会遇到很多关于由于对非常大的代码库维护不当而导致的性能和内存泄漏的问题。我们在一个 Linux 机器上启动了近 250 多个 Erlang 节点来模拟大约 500 到 1000 多个电信节点。

通过静态检查代码来查找内存泄漏是一项非常困难的任务。相信我,我发现在运行时本身很容易找到内存泄漏。

什么是内存泄漏?
从一般操作系统的角度来看,一些已分配但未使用的内存,就是内存泄漏。从 Erlang 的角度来看,Erlang 缺少内存,总是向操作系统请求内存(它应该很丰富)。如果分配的内存没有被 Erlang 正确使用,我们称之为内存泄漏。

内存分配

当应用程序运行时,它会从操作系统中获得足够的 RAM。运行应用程序的 erlang VM 同样负责在不使用时归还该内存。但并非每次都如此,尤其是大型代码库、大型应用程序且维护不善时。

识别内存泄漏

当您在 Erlang 节点上启动应用程序时,它通常会从操作系统消耗一些“x MB”的 RAM。您可以使用“top”命令查看 RAM 消耗,如下所示。

top -p <beam PID>

RSS 字段给出了驻留内存大小,即最小 RAM 消耗,而 Virt 字段给出了当前运行时 RAM 消耗。

如果您在用例执行之前和之后记录这些值。它应该几乎是一样的。当我说几乎时,这意味着您可以预期这些值会略有增加,因为您的用例可能会将一些值添加到 ETS 或 Mnesia(ram_copies) 中,这些值很可能存储在 RAM 中,并且可能会略微增加消耗。

分析

现在,即使在用例完成后,如果您发现 RAM 利用率有很大差异(在我的情况下 > 20MB)。让我们的冒险从这里开始:

使用 erlang:memory() 在用例运行前后获取RAM使用的快照。

erlang:memory() 函数为您提供以字节为单位的内存利用率,如 ets、atom、binary、proc_memory 等,应检查每个数据类型在用例后是否增加。您可以立即怀疑 ETS、二进制、atom_table 和进程内存。

一些可能对我们有帮助的功能

要查找所有进程内存,请使用以下命令:

[io:format(“~p:~p: ~p~n”,[Pid,erlang:process_info(Pid,current_function),erlang:process_info(Pid,memory)])||Pid<-erlang:processes()].

要查找所有 ets 表内存和条目,请使用以下命令:

[io:format(“~p -> ~p: ~p mem ~p ~n”,[E,ets:info(E,name),ets:info(E,size),ets:info(E,memory)])||E<-ets:all()],calendar:local_time().

有时你可能需要查找 message_queue_len,使用如下:

[io:format(“~p:~p: Q:~p ~p~n”,[Pid,erlang:process_info(Pid,current_function),erlang:process_info(Pid,message_queue_len),erlang:process_info(Pid,memory)])||Pid<-erlang:processes()].

我们还可以添加一些排序功能来对上述结果的结果进行排序。但我并不是保持一个干净的类型,我总是喜欢浏览整个结果以寻找更多线索。

在上述情况下,如果你仔细观察,我尝试将每个进程的当前函数连同内存一起打印出来。这个 current_function 很可能是高内存消耗进程的蜜罐,您可以从中开始调查。我可以很容易地向您保证,这种内存泄漏情况很可能就会在该函数周围。

快速解决方法: 在您发现/怀疑内存泄漏的代码中添加 erlang:garbage_collect(self()) 将是检查/修复内存泄漏的最快方法。 但这确实不是一个好的解决方法。更频繁地使用garbage_collect 会占用CPU。相反,我们应该尝试修复代码。

一些内存泄漏案例:

案例 1:如果您的应用程序中有自己的跟踪机制,并且您的应用程序总是为您格式化一个很好的跟踪信息,但仅在启用跟踪时才打印它们。这可能形成了内存泄漏。

案例2:假设我们有一个函数“A”,里面有一个接收循环,函数“B”有一个接收循环,如果你在函数“B”的接收循环中调用这个函数“A”。进入此接收循环的任何数据都无法找到进行garbage_collection 的方式。

案例 3:我还发现 gen_event 行为中的 fetch_msg 函数会保留一些内存,但我们只能在大块数据分配时看到。

Related Posts

2021 年你需要知道的关于 Erlang 的一切

今天,我们将看一个相当古老且有些古怪的东西。 你们大多数人可能没有注意到的语言。 虽然 Erlang 不像某些现代编程语言那样流行,但它安静地运行着 WhatsApp 和微信等每天为大量用户提供服务的应用程序。 在这篇文章中,我将告诉你关于这门语言的更多事情、它的历史,以及你是否应该考虑自己学习它。 ## 什么是 Erlang,它在哪里使用? Erl

Read More

Erlang JIT中基于类型的优化

这篇文章探讨了 Erlang/OTP 25 中基于类型的新优化,其中编译器将类型信息嵌入到 BEAM 文件中,以帮助JIT(即时编译器)生成更好的代码。 ## 两全其美 OTP 22 中引入的基于SSA的编译器处理步骤进行了复杂的类型分析,允许进行更多优化和更好的生成代码。然而,Erlang 编译器可以做什么样的优化是有限制的,因为 BEAM 文件必须

Read More

Erlang JIT之路

自从Erlang 存在,就一直有让它更快的需求和野心。这篇博文是一堂历史课,概述了主要的 Erlang 实现以及如何尝试提高 Erlang 的性能。 ## Prolog 解释器 Erlang 的第一个版本是在 1986 年在 Prolog 中实现的。那个版本的 Erlang 对于创建真正的应用程序来说太慢了,但它对于找出Erlang语言的哪些功能有用,哪

Read More