【问题标题】:C# Manual delete object pointerC#手动删除对象指针
【发布时间】:2018-09-21 02:36:31
【问题描述】:

为什么不在 C# 中创建删除对象的可能性,就像在 C++ 中一样,作为替代但不是必需的操作?

例如,当我们想从 500 Mb 对象中清理内存时,不再需要等待 GC(垃圾收集器)

【问题讨论】:

  • GC.Collect();GC.Collect();叫它两次。但问题是你为什么在乎?
  • 如果这 500 Mb 来自非托管资源,请检查您的 IDisposable 对象,否则检查元素的范围(静态 vs 类 vs 方法)是否正确,并且不再分配给 null 元素在该范围内使用。
  • 像.NET 这样的托管 语言的重点在于,有一个 垃圾收集器可以删除所有孤立对象。所以你不必关心这个 mch,除非你有一些 GC 根本无法处理的 unmanaged 资源。
  • 这个时候你需要看看Garbage Collection

标签: c# garbage-collection


【解决方案1】:

.NET 等托管语言的全部意义在于,有一个垃圾收集器可以删除所有孤立对象。这些都是不存在引用的对象。例如,在以下示例中,当程序到达DoSomething 方法的末尾时,对创建的MyClass 类型对象的所有引用都将丢失,并且对象本身被标记 删除。然而,GC 决定何时执行此操作。

void SoSomething()
{
    var m = new MyClass();
}

所以你不必关心这么多,除非你有一些非托管资源,GC 根本无法处理。

所以你的程序似乎有一些大的设计问题。您要么在 GC 状态 2 中有太多对象,这些对象是长期存在的,因此未被收集(例如static 变量),或者有许多非托管资源,例如 GC 无法处理的文件处理程序。

在第一种情况下,您应该尽可能地限制变量的范围。

在第二种情况下,您应该对每个此类资源使用Dispose 方法。使用using 最容易做到这一点,它会在发生异常或代码块完成时自动调用IDisposable.Dipose()。调用Dispose 将确保释放非托管 内存。但是,对象的剩余 托管 部分仍保留在内存中。然而,一旦所有对它的引用消失,它很快就会被垃圾收集,因此变量范围的重要性。

using (FileStream fs = File.Create(path))
{
    // do something with the file
}

【讨论】:

  • 我建议:1-为第二种情况的IDisposable 添加docs.microsoft.com/en-us/dotnet/csharp/language-reference/…。 2- 提到GC.Collect() 不是魔法,是资源没有关闭/处置它不会被释放。
  • 建议甚至可能很愚蠢,但至少有一个实例,DoSomething 示例 wouldnt 在方法之后可回收:如果 MyClass 的构造函数将新创建的对象 (this) 添加到静态集合...尽管第二点将涵盖这一点。只是在发生其他事情时指出规则有例外
  • @pinkfloydx33 你提到的是第一种情况,可变作用域/长寿命(static IDisposable)对象不会被GB回收,应该手动处置。
【解决方案2】:

你可以使用 GC.Collect();强制垃圾收集器进行收集,但通常不建议这样做,除非您有充分的理由并且您知道自己在做什么。

如果您有办法到达要销毁的对象,实际上不可能销毁该对象-收集器不会收集可到达的对象。

如果您要销毁的对象是非托管对象,则使用 using 对其进行包装,或者记得将其丢弃。

【讨论】:

    【解决方案3】:

    我将添加我的 50 美分关于 GC。如果你有很多大的非托管对象,.NET 不知道你吃了多少内存,你需要添加Memory Pressure 来通知实际分配了多少字节:

    GC.AddMemoryPressure(long bytes)
    

    一个例子可以在这里找到:

    http://adavesh.blogspot.com/2012/02/gcaddmemorypressure-working-with-native.html

    【讨论】:

    • 我不知道存在 :-) 我不是 OP 但谢谢。
    • 最适合 cmets,但 GC.AddMemoryPressure 真的很有趣。我想知道是否必须使用它是代码异味或资源量不足的迹象。
    • @bradbury9 人们通常不知道它并认为他们有内存泄漏并调用 GC.Collect 来修复这个所谓的“内存泄漏”。如果您必须处理通常由系统类处理的大型非托管资源,并且在大多数情况下您看不到它在后台是如何工作的,那么您必须使用它。如果你使用它,这不是坏代码的标志。
    【解决方案4】:

    如果您正在使用的对象在其类中实现IDisposable,那么您可以调用my_object.Dispose(); 来手动移除内存分配。

    您也可以使用System.GC.Collect() 强制垃圾收集器运行,但不建议这样做。

    【讨论】:

      【解决方案5】:

      这就是分代 GC 的工作原理。与 C++ 不同,它没有可用空间映射来分配新对象。它只是在先前分配之后在内存空间中分配新对象。当 GC 发生时,仍然需要保留在内存中的对象会被压缩并在内存中物理移动。

      因此,即使您可以释放单个对象,内存分配器也不会使用该内存段,直到下一次 GC 发生,并且只释放一个对象是没有意义的。

      当然,GC中还有更多的细节,比如不同代、大对象堆、多代多段,但总体上只删除一个对象是没有用的。

      【讨论】:

        【解决方案6】:

        在允许显式释放对象的语言中,对已释放对象的引用可能会自发地成为对不相关对象的看似有效的引用。例如,在 C 中,给定:

        FILE *f = fopen("file1", "w");
        ...
        fclose(f);
        FILE *g = fopen("file2", "w");
        ...
        fprintf(f,"Hey there!");
        

        有可能在执行 fprintf 时,由f 标识的FILE 可能已被回收用于file2。一般来说,语言没有便宜的方法来防止此类错误。

        基于可达性的 GC 的主要好处之一是这样的事情根本不会发生。如果有人执行:

        someStreamType f = openStreamSomewhow(...);
        f.Dispose();
        someStreamType g = openStreamSomewhow(...);
        f.outputData(...);
        

        然后在调用outputData 时,f 将持有对已处理流对象的引用。只要该引用的任何副本存在于 Universe 中的任何位置,它就会继续识别同一个死对象。

        Java 和 .NET 都不能安全地回收对象使用的存储空间,除非它们可以确定不存在对它的引用。同时对大量对象进行这种确定比对单个对象进行这种确定要便宜得多,因此被告知可能不存在对某些特定对象的引用并不是很有帮助。

        【讨论】:

          猜你喜欢
          • 2011-05-02
          • 1970-01-01
          • 2013-09-09
          • 2012-09-10
          • 2015-08-07
          • 2011-06-10
          • 2023-03-26
          • 2023-03-29
          • 2015-03-08
          相关资源
          最近更新 更多