【问题标题】:How do I handle an object moving in memory in the middle of a method?如何在方法中间处理在内存中移动的对象?
【发布时间】:2013-04-27 21:01:03
【问题描述】:

我正在用 C++ 编写一个用于编程语言的 VM。该语言是垃圾收集的,因此我在垃圾收集堆中分配了 C++ 类的实例。我正在使用复制收集器,因此当发生 GC 时,这些对象会在内存中移动。这意味着需要更新指向该对象的每个指针。 大部分这些指针都很容易处理,除了一个棘手的:this。考虑:

class SomeObj : public Managed      // inheriting from this means it's 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 仍然指向旧的错误位置。

我可以通过不在托管内存中的类上使用实例方法来解决这个问题,但我确实喜欢这样的代码更简单。有什么技术可以解决这个问题吗?

【问题讨论】:

  • this 只是method 的隐藏参数。你可以做你喜欢的事,比如说,你可以delete this;,只要它被适当地分配并且之后你不能(直接或间接地)访问它。
  • 请注意,您需要担心的不仅仅是this,它是任何本地指针变量:void SomeObj::method() { int *x = &m_int; do_something_that_causes_GC(); use(x); }(编译器可能会将x 放入寄存器中。)
  • 如果你有一棵二叉树(每个节点有 2 个指针),什么代码在 GC 后修复所有指针?我认为复制收集器需要引入另一个级别的间接性。
  • 如果你知道this在哪里,你的GC可以更新它,它要么在堆栈上,要么在寄存器中。是你不知道this在哪里的问题吗?如果是这样,那么我认为你有更严重的问题。
  • 你看过 Boehm 保守的垃圾收集器吗?做GC但不移动对象,被广泛使用。

标签: c++ garbage-collection programming-languages vm-implementation


【解决方案1】:

您的 GC 需要扫描堆栈和寄存器以查找指针并修复它们。如果您的 VM 支持多线程,则需要在扫描其堆栈时暂停所有线程。 '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,r1 是如何修复的?

【讨论】:

  • 其他指针在这里没有任何问题。我可以将它们包装在一个结构中,使它们成为可见的根,GC 将能够直接找到它们。
  • @munificent 因此,在使用 this 指针调用任何 C++ 方法之前,请将 this 指针包装在同一个结构中。
  • 鉴于this 的语义已融入 C++,我不知道该怎么做。我错过了什么?
  • 我认为这行不通。在method 的主体内,this 仍将永久绑定到指向对象的原始原始指针。我能做的不是使用实例方法,而是使用SomeClass::method(gc<SomeClass> self)。但如果可能的话,我想避免这种情况。
  • @munificent 你的虚拟机是多线程的吗?进行 GC 时是否停止所有线程?请参阅下一个编辑,了解为什么这很重要。
【解决方案2】:

您可以引入另一个级别的间接性。我会用你的例子:

class SomeData : public Managed
{
 int someField;
};

class SomeObj : public Managed      // inheriting from this means it's 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;
};

请注意,托管的每个实现都会这样做。

【讨论】:

  • 这段代码是不是也有同样的问题? GC移动当前对象,this不再有效,someData的访问崩溃。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-11-03
  • 2010-09-10
  • 2019-10-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多