Я прокладываю себе путь через Хорька (порт Ruby Lucene) код для решения ошибки. Код хорька является главным образом расширением C Ruby. Я сталкиваюсь с некоторыми проблемами со сборщиком "мусора". Мне удалось зафиксировать его, но я не полностью понимаю свою фиксацию =) Я надеюсь кто-то с более глубоким знанием Ruby и расширения C (это - мой 3-й день с Ruby), может уточнить.Спасибо.
Вот ситуация:
Некоторые, куда у Хорька C код, я возвращаю "Маркер" в землю Ruby. Код похож
static VALUE get_token (...)
{
...
RToken *token = ALLOC(RToken);
token->text = rb_str_new2("some text");
return Data_Wrap_Struct(..., &frt_token_mark, &frt_token_free, token);
}
frt_token_mark называет rb_gc_mark (маркер-> текст), и frt_token_free просто освобождает маркер со свободным (маркер)
В Ruby этот код коррелирует к следующему:
маркер = @input.next
В основном @input установлен на некоторый объект, называние следующего метода на нем инициировало get_token C вызов, который возвращает маркерный объект.
На земле Ruby я затем делаю что-то как w = token.text.scan ('\w + ')
Когда я выполняю этот код внутри некоторое время 1 цикл (для изоляции моей проблемы), в какой-то момент (примерно, когда мое рубиновое место мадам процесса переходит к 256 МБ, вероятно, некоторый порог GC), Ruby умирает с ошибками как
метод проверки обратился к завершенному объекту
Или просто дампы ядра. Мое предположение было то, что token.text был собран "мусор".
Я не знаю достаточно о Ruby C расширение для знания то, что происходит с возвращенными объектами Data_Wrap_Struct. Кажется мне, присвоение на земле Ruby, маркер =, должно создать ссылку на него.
Мое "обходное решение" / "фиксация" должна создать переменную экземпляра Ruby в объекте, упомянутом @input, и хранит маркерный текст там, для получения дополнительной ссылки на него. Таким образом, код C похож
RToken *token = ALLOC(RToken);
token->text = rb_str_new2(tk->text);
/* added code: prevent garbage collection */
rb_ivar_set(input, id_curtoken, token->text);
return Data_Wrap_Struct(cToken, &frt_token_mark, &frt_token_free, token);
Таким образом, теперь я создал "curtoken" во входной переменной экземпляра и сохранил копию текста там... Я заботился для удаления/удаления этой ссылки в бесплатном обратном вызове класса для @input.
С этим кодом это работает в этом, я больше не получаю завершенную объектную ошибку.
Фиксация, кажется, имеет смысл мне - это сохраняет дополнительное касательно в curtoken к строке token.text, таким образом, экземпляр token.text не будет удален до следующего раза @input.next называют (в котором времени другой token.text заменяет старое значение в curtoken).
Мой вопрос: почему это не работало прежде? Не был должен Data_Wrap_Structure возвращать объект, который при присвоении на земле Ruby имеет действительную ссылку и не быть удаленным Ruby?
Спасибо.
При вызове сборщика мусора Ruby, он имеет фазу отметки и фазу зачистки. Фаза разметки отмечает все объекты в системе путем разметки:
а также ряда других объектов, которые не имеют значения для этого обсуждения. Затем на этапе развертки уничтожаются все недоступные объекты (т.е. те, которые не были помечены).
Data_Wrap_Struct возвращает ссылку на объект. Пока эта ссылка доступна в рубиновом коде (например, хранится в локальной переменной) или находится на стеке (на него ссылается локальная переменная C), объект не должен подвергаться подметанию.
Это выглядит так, как будто из того, что вы разместили, токен->текст собирает мусор. Но почему он собирается? Он не должен быть помечен. А сам объект Жетона помечен? Если да, то текст с токеном должен быть помечен. Попробуйте установить точку останова или распечатать сообщение в функции маркеровки, чтобы увидеть.
Если маркер не будет помечен, то следующий шаг - выяснить, почему. Если маркер помечен, то следующий шаг - выяснить, почему подметается строка, возвращаемая методом text() (возможно, это не тот же самый объект, который помечается).
Также, Вы уверены, что именно текстовый член маркера вызывает исключение? Глядя на:
http://github.com/dbalmain/ferret/blob/master/ruby/ext/r_analysis.c
я вижу, что и маркер, и поток маркеров имеют text() методы. Структура TokenStream не содержит ссылки на свой текстовый объект (не может, т.к. это C-структура без знания рубина). Таким образом, объект Ruby, обёртывающий структуру C, должен содержать ссылку (и это делается с помощью rb_ivar_set).
Структура RToken не должна этого делать, так как она помечает свой текстовый член в своей маркерной функции.
Еще одна вещь: вы можете иметь возможность воспроизвести эту ошибку, явно вызывая GC.start в своем цикле, вместо того, чтобы выделять так много объектов, что мусорный коллектор сработает. Это не исправит проблему, но может упростить диагностику.