Lua утечка памяти слабых таблиц - PullRequest
3 голосов
/ 05 августа 2020

Я не часто использую слабые таблицы. Однако теперь мне нужно управлять определенными атрибутами для моих объектов, которые должны храниться в другом месте. Вот тогда и пригодятся слабые столы. Моя проблема в том, что они не работают, как ожидалось. Мне нужны слабые ключи, чтобы вся пара ключ / значение удалялась, когда на ключ больше не ссылаются, и мне нужны сильные значения, поскольку хранятся таблицы с метаинформацией, которая используется только внутри этой таблицы, которая также имеет ссылка на ключ, но почему-то эти пары никогда не собираются.

Пример кода:


local key = { }
local value = {
        ref = key,
        somevalue = "Still exists"
}

local tab = setmetatable({}, { __mode = "k" })

tab[key] = value

function printtab()
        for k, v in pairs(tab) do
                print(v.somevalue)
        end
end

printtab()

key = nil
value = nil

print("Delete values")
collectgarbage()

printtab()

Ожидаемый результат:

Still exists
Delete values

Получено:

Still exists
Delete values
Still exists

Почему не удаляется пара ключ / значение? Единственная ссылка на значение - это, по сути, слабая ссылка внутри вкладки, а ссылка внутри значения не имеет значения, поскольку само значение нигде не используется.

Ответы [ 3 ]

2 голосов
/ 05 августа 2020

Таблицы эфемеронов поддерживаются начиная с Lua 5.2. В руководстве Lua 5.2 говорится:

Таблица со слабыми ключами и сильными значениями также называется таблицей эфемеронов. В таблице эфемеронов значение считается достижимым, только если доступен его ключ. В частности, если единственная ссылка на ключ проходит через его значение, пара удаляется.

Lua 5.1 не поддерживает корректно таблицы эфемеронов.

1 голос
/ 05 августа 2020

Вы делаете слишком много предположений относительно сборщика мусора. Ваши данные будут собраны в конечном итоге . В этом конкретном примере он должен работать, если вы дважды вызываете collectgarbage(), но если у вас есть несколько циклов в вашей слабой таблице, это может занять еще больше времени.

EDIT: это действительно имеет значение только тогда, когда вы ждете __cg событие

Я просмотрел ваш код более подробно и заметил, что у вас есть другая проблема.

Ваше значение также ссылается на ключ, создавая al oop, что, вероятно, слишком много для обработки G C вашей Lua версии. В PU C Lua 5.3 это работает, как и ожидалось, но в LuaJIT l oop, похоже, не позволяет собирать значение.

Это действительно имеет большой смысл, если подумать; насколько я могу судить, все работает: сначала удаляются слабые элементы из таблицы, когда на них больше нет ссылок, и, таким образом, их можно собирать в обычном режиме при следующем запуске G C.

Однако, когда этот шаг выполняется, ключ все еще находится в таблице, поэтому (неслабое) значение является действительной ссылкой в ​​глазах сборщиков мусора, поскольку оно доступно из кода. Таким образом, тип G C заходит в тупик и не может удалить пару ключ-значение.

Возможные решения:

  1. Не сохраняйте ссылку на введите значение
  2. Сделайте значение слабой таблицей, чтобы оно не считалось ссылкой.
  3. Обновление до другой Lua версии
  4. Оберните ссылку в одноэлементном массиве со слабыми значениями
0 голосов
/ 05 августа 2020

вы можете изменить код вот так. Тогда вы получите ожидаемый результат. советы: не ссылайтесь на ключевую переменную, если хотите, чтобы это была неделя.

local key = { }
local value = {
        -- ref = key,
        somevalue = "Still exists"
}

local tab = setmetatable({}, { __mode = "k" })

tab[key] = value

function printtab()
        for k, v in pairs(tab) do
                print(v.somevalue)
        end
end

printtab()

key = nil
value = nil

print("Delete values")
collectgarbage()

printtab()
...