当心使用erlang的monitor

概述

要从一次线上的内存泄漏提及. 最终定位到在异步send方法中, monitor了被调用进程, 而又没有释放, 致使泄漏. 违反直觉的是, 这个泄漏是双向的, A monitor B后, A会记录 monitors B, B会记录 monitored_by A. 进程A退出后, 进程B不会释放 monitored_by A. 因此, 在使用monitor时, 注意在任何状况下, 都要demonitor.html

如何定位 monitor 的泄漏

monitor 占用的内存被统计为 system/process, 属于std_alloc分配器.
能够用process内存排序, 找出泄漏的进程.git

:recon.proc_count(:memory, 10)

接着, 能够用process_info, 查看可疑的进程. monitors是monitors的进程, monitored_by是被哪些进程monitor.程序员

iex(xxxx@xxxx.)51> {_, b} = :erlang.process_info(pid(0,3182,0), :monitors) {:monitors,  [#PID<55192.4940.0>, #PID<55192.4940.0>, #PID<55192.4940.0>, #PID<55192.4940.0>, #PID<55192.4940.0>, #PID<55192.4940.0>, #PID<55192.4940.0>, #PID<55192.4940.0>, #PID<55192.4940.0>, #PID<55192.4940.0>, #PID<55192.4940.0>, #PID<55192.4940.0>,

此外, 还能够用allocated_types, :erlang.memory, 看到system, std_alloc有大量泄漏.github

iex(xxxx@xxxx.)35> :recon_alloc.memory(:allocated_types) [  binary_alloc: 7634944, driver_alloc: 6324224, eheap_alloc: 58241024, ets_alloc: 10780672, fix_alloc: 4751360, ll_alloc: 96993280, sl_alloc: 557056, std_alloc: 830242816, temp_alloc: 2228224 ]
iex(xxxx@xxxx.)4> :erlang.memory [  total: 28235126584, processes: 441032432, processes_used: 440339304, system: 27794094152, atom: 1982841, atom_used: 1971208, binary: 15334152, code: 46042580, ets: 28443288 ]

若是怀疑monitor有泄漏, 能够直接根据monitors/monitored_by列表长度排序:异步

Enum.map(:erlang.processes(), fn proc -> {:erlang.process_info(proc, :monitors), proc} end) |> Enum.filter(fn v -> elem(v, 0) != :undefined end) |> Enum.map(fn v -> {length(elem(elem(v, 0), 1)), elem(v, 1)} end) |> Enum.sort(fn({a, _}, {b, _}) -> a > b end) |> Enum.take(100)

Enum.map(:erlang.processes(), fn proc -> {:erlang.process_info(proc, :monitored_by), proc} end) |> Enum.filter(fn v -> elem(v, 0) != :undefined end) |> Enum.map(fn v -> {length(elem(elem(v, 0), 1)), elem(v, 1)} end) |> Enum.sort(fn({a, _}, {b, _}) -> a > b end) |> Enum.take(100)

一些思考

为何monitor是双向的:

显然 A monitor B, 双方都要记录, 不然atom

  1. A不知道monitor了B
  2. B退出时没法通知A
为何进程A退出时, 不通知全部被monitor的进程

进程A退出时, 必然要通知全部被monitor的进程释放这块内存. 我的认为在语言层面实现会更好. erlang没作这点, 只要程序员确保必定demonitor. 就没有问题.
在节点连接断开时, monitored_by的内存是会被释放的. 假如erlang连这点都没能保证, 不管如何都没法实现正确了.code

参考

https://erlang.org/doc/man/er...
https://ferd.github.io/recon/...htm

相关文章
相关标签/搜索