如何在使用NIF时正确收集垃圾

时间:2012-01-31 15:52:42

标签: erlang ffi

我原以为如果我所做的只是1)加载NIF库,2)执行我的新/ 0方法,3)通过F()释放所有内容,然后4)erlang:garbage:collect()我将回到我从记忆方面开始的地方。事实上,我正在泄露记忆。显然,我的代码是最可能的嫌疑人。

有人能告诉我我做错了吗?

我有以下结构:

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

我的负载会打开资源

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);
}

和destruct-或资源如下:

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

    enif_release_resource(handle);
    handle = NULL;
}

1 个答案:

答案 0 :(得分:3)

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

这可能不会发生,因为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()项足够大,它将会超出其他内存分配),并在之后泄露。