Как обрабатывать объект, перемещающийся в памяти в середине метода?

71
8

Я пишу VM в C++ для языка программирования. Язык - это сбор мусора, поэтому у меня есть экземпляры классов C++, которые выделяются в кучу мусора. Я использую копировальный коллектор, поэтому, когда происходит GC, эти объекты перемещаются в памяти. Это означает, что каждый указатель на этот объект должен быть обновлен. Большинство этих указателей просты в обращении, за исключением одного сложного: this. Рассматривать:

class SomeObj : public Managed      // inheriting from this means it on the GC heap
{
public:
void method()
{
SomeObj* other = new SomeObj(); // could trigger a GC.
printf("%d\n", someField); // this points to wrong memory
}

private:
int someField;
};

Если я посередине метода экземпляра какого-либо объекта, который живет на куче GC, то this указывает на некоторую GC-память. Коллекция может встречаться в середине этого метода. Когда это произойдет, объект перемещается в новое место. Но, поскольку мы находимся в середине вызова метода, this все еще указывает на старое неправильное местоположение.

Я мог обойти это, не используя методы экземпляра в классах, которые находятся в управляемой памяти, но мне нравится, что код проще. Существуют ли какие-либо методы борьбы с этим?

спросил(а) 2013-04-28T00:01:00+04:00 7 лет, 6 месяцев назад
1
Решение
70

Ваш GC должен сканировать стек и регистры для указателей и исправить их. Если ваша виртуальная машина поддерживает многопоточность, вам необходимо приостановить все потоки при сканировании их стеков. Указатель 'this' будет находиться в стеке или в регистре.

Поскольку C++ не предоставляет информацию о типе для стека, вам может быть трудно с чем-то вроде

int i = 1000000;
char * p = new char[10]; // 0xF4240 = 1000000

Любой метод, который вы используете для перемещения других указателей, будет иметь одинаковую проблему. В какой-то момент ваш код должен преобразовывать дескрипторы в указатели, и эти указатели должны быть исправлены.

Измените C++ код следующим образом:

func()->method()

выглядеть

struct GCroot call123 = { func() };
call123.obj->method();

Многопоточная проблема. Если у вас есть такой код

struct GCroot obj123 = { /* .. */ };
obj123.ptr->x = obj123.ptr->x + 1;

он может генерировать псевдо-ассемблерный код, подобный этому

load r1, obj123.ptr
load r2, (r1)
add r2, 1
store (r1), r2

если другой поток выполняет GC в любое время между первой и последней линиями asm, как r1 фиксируется?

ответил(а) 2013-04-28T15:36:00+04:00 7 лет, 6 месяцев назад
57

Вы можете ввести другой уровень косвенности. Я буду использовать ваш пример:

class SomeData : public Managed
{
int someField;
};

class SomeObj : public Managed // inheriting from this means it on the GC heap
{
public:
void method()
{
SomeObj* other = new SomeObj(); // could trigger a GC.
printf("%d\n", someData->someField); // this points to wrong memory
}

private:
SomeObjData* someData;
};

Обратите внимание, что каждая реализация управляемых будет делать это.

ответил(а) 2013-04-28T01:42:00+04:00 7 лет, 6 месяцев назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема