【问题标题】:What would a garbage collector do here?垃圾收集器会在这里做什么?
【发布时间】:2010-06-20 07:31:07
【问题描述】:

我在另一个线程中看到了这段代码

void foo {
  int *n = malloc(sizeof(int)); 
  *n = 10; 
  n++;
  printf("%d", *n);
}

这里的错误很明显。 n 没有被取消引用。 存在内存泄漏。 让我们假设有一个垃圾收集器在这里工作。对我们初始值n 的引用计数现在为零,因为n 不再引用它。所以它是垃圾并返回了。但是n 指向的新位置呢?从技术上讲,这个内存区域尚未分配。那么这里的引用计数会不会增加呢?

【问题讨论】:

  • 你在想我想的同一个想象中的垃圾检测器吗?
  • 为什么其他人删除了他的答案?
  • 程序不太可能继续超过 printf,因为您取消引用无效指针,GC 或没有 GC。
  • @kaizer.se:内存是按块分配的,并且块可能大于实际请求的内存量。因此,该程序可能工作得很好 - 取决于编译器。

标签: c memory-management garbage-collection


【解决方案1】:

正确实现的垃圾收集器将按如下方式工作:

int *pi = malloc(sizeof int);
*pi = 10;

这里一切都很好。

pi++;

此语句让pi 指向分配的 int 后面,这是 ISO C99 标准明确允许的(参见 6.5.6p7、6.5.6p8)。后面代码中可能有pi--,所以分配的int还是可以访问的。或者它可以通过表达式pi[-1] 访问,此时它完全有效。

pi = NULL;

此时,无法再访问分配的 int,因此垃圾收集器可能会收集内存。

总而言之:所有指向对象开头、对象中间某处或对象后面位置的指针都可用于访问该对象。因此,如果内存中存在这样的值,则该对象一定不能被垃圾回收。

【讨论】:

  • 好吧,如果你实现你的一个 GC,你仍然可以这样做,但是 6.5.6p8 只是说操作 p+k 在分配的内存+2 之前不能溢出,这样你总是有 p + last_el
  • “那个”是什么意思?我没看懂你的说法。如果 6.5.6p8 不存在,分配的 int (pi + 1) 之后的位置可能是无效的,所以指向此位置的指针不会被视为与 pi 相关。
  • .. 6.5.6p8 明确指出 pi+1 是无效的:“结果指向数组对象的最后一个元素,它不应用作一元 * 运算符的操作数被评估”对我来说,该段的重点是 addition pi+1 不能溢出,因此 pi
  • 表达式 pi + 1 有效(在递增之前),但 *(pi + 1) 无效。这正是 6.5.6p8 所指出的。该段落指出表达式pi + 1 仍然表示与分配的int 相关的指针。
【解决方案2】:

C 垃圾收集器不执行引用计数。它们通常是标记扫描,并且它们对块而不是单个字节进行操作。在您的示例中, gc 将标记块,而不是地址。即便如此,您问题的核心仍然有效:

mark 阶段在到达“坏”指针时会做什么?
保守的 收集器会简单地忽略它。初始 malloc 中的内存将被收集。

【讨论】:

    【解决方案3】:

    我改变主意了;我想是的。问题是这样的代码:

    int* n = malloc(sizeof(int));
    n++;
    int* o = n;
    int* p = n;
    n = malloc(sizeof(int));
    

    现在有三个对n 指向的内存的引用,但垃圾收集器只看到一个。尽管存在op 指针,但执行n = NULL; 会导致清理。像这样的指针灵活性是 C 垃圾收集器如此困难的主要原因。我不确定是否有可能在 C 中拥有完美的垃圾收集器,因为您可以拥有直到运行时才知道地址的指针

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-11-10
      • 1970-01-01
      • 2020-03-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多