erlang - 使用 NIF 时如何正确进行垃圾收集

标签 erlang ffi

我会想,如果我所做的只是 1) 加载一个 NIF 库,2) 执行我的 new/0 方法,3) 通过 F() 释放一切,然后 4) erlang:garbage:collect()我会回到内存的起点。事实上,我正在泄漏内存。显然,我的代码最有可能受到怀疑。

谁能告诉我我做错了什么?

我有以下结构:

typedef struct Node
{
  int Key;
  ERL_NIF_TERM TermPtr;
  struct Node *Next;
} Node;

我的on-load打开资源

int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
{
    ErlNifResourceFlags flags = (ErlNifResourceFlags)(ERL_NIF_RT_CREATE | ERL_NIF_RT_TAKEOVER);
    NODE_RESOURCE = enif_open_resource_type(env,  "linkedlist_nif", 
                       "node_resource",
                       &node_dtor,
                       flags,
                       0);
}

new/0 被映射到这个 NIF:

static ERL_NIF_TERM new_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
 ERL_NIF_TERM term;
  Node *Head =  (Node *)enif_alloc_resource(NODE_RESOURCE,sizeof(Node));

  Head->Next = 0;
  term = enif_make_resource(env, Head);
  enif_release_resource(Head);

  return enif_make_tuple2(env, enif_make_atom_len(env, "ok", 2), term);
}

资源的析构或运算如下:

static void node_dtor(ErlNifEnv* env, void* arg)
{
    Node* handle = (Node*)arg;

    enif_release_resource(handle);
    handle = NULL;
}

最佳答案

因此在 shell 中,您使用 A = your_nif:new() 创建一个值。 然后您使用 f() 释放该值。 然后您 erlang :garbage_collect()。 并且您希望您的内存使用量与开始之前相同(大概)?

这可能不会发生,因为 shell 保留了先前执行结果的历史记录。这将在整个 f().garbage_collect(). 中保持对术语的引用,因此您不会看到您期望的内存行为。

你可以这样做:

Before = erlang:memory().
Pid = spawn( fun () ->
  A = your_nif:new(),
  receive
    cleanup -> ok
  end
 end).
timer:sleep(timer:seconds(1)). % Wait for pid to spawn and allocate term.
During = erlang:memory().
Pid ! cleanup.
After = erlang:memory().

lists:zipwith3(fun ({K, B}, {K, D}, {K, A}) -> 
                 {K, _Values = {B, D, A}, {_Leaked = A - B, _Used = D - B}} end,
               Before, During, After).

这应该向您显示测试期间使用的内存的一些近似值(严格来说并不准确,但如果 your_nif:new() 项足够大,它将超过其他内存分配),并且泄漏之后。

关于erlang - 使用 NIF 时如何正确进行垃圾收集,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9082542/

相关文章:

erlang - 如何有效地使用 gb_trees?

multithreading - 从技术上讲,为什么 Erlang 中的进程比操作系统线程更高效?

types - 为什么 Dialyzer 告诉我这个有趣的合约有重叠的域?

c - 使用 FFI 链接到 Rust 的本地 C 库

encoding - 如何用字符串更新 libc::c_char 数组?

rust - 将包含从 Rust 到 C 的函数指针的零终止数组的 C 符号公开

haskell /FFI : how to handle external dll crashes

erlang - 收集有关 simple_one_for_one worker 的信息

rust - 如何调用返回多种类型的不同函数指针的 C 函数?

memory-management - 减少RabbitMQ内存使用