Как правильно собирать мусор при использовании NIF - PullRequest
3 голосов
/ 31 января 2012

Я бы подумал, что если я все, что я делаю, это 1) загружаю библиотеку NIF, 2) выполняю мой новый метод / 0, 3) освобождаю все через F (), а затем 4) erlang: garbage: collect ()что я вернусь туда, откуда я начал в отношении памяти.На самом деле у меня течет память.Ясно, что мой код наиболее вероятен.

Может кто-нибудь сказать мне, что я делаю неправильно?

У меня следующая структура:

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

Мой on-загрузка открывает ресурс

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-or для ресурса выглядит следующим образом:

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

    enif_release_resource(handle);
    handle = NULL;
}

1 Ответ

3 голосов
/ 01 февраля 2012

Таким образом, в оболочке вы создаете значение с помощью A = your_nif:new()., затем освобождаете значение с помощью f()., затем erlang:garbage_collect(). и ожидаете, что ваше использование памяти будет таким же (ish), как и до начала?

Скорее всего, этого не произойдет, поскольку оболочка хранит историю результатов предыдущих выполнений. Это сохранит ссылки на этот термин во всех 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() достаточно велик, он затмит другое выделение памяти), а затем просочился.

...