这些人如何避免制造任何垃圾?

Car*_*los 16 c# garbage-collection finance

这是我在网上发现的一篇有趣的文章.

它讨论了该公司如何能够在托管环境中解析大量财务数据,主要是通过对象重用和避免不可变因素(如字符串).然后他们继续说明他们的程序在连续操作阶段没有做任何GC.

这是非常令人印象深刻的,我想知道这里是否有其他人有关于如何做到这一点的更详细的指导.首先,我想知道如何避免使用字符串,当消息中的某些数据是字符串时,无论客户端应用程序正在查看消息,都希望传递这些字符串?另外,你在启动阶段分配了什么?你怎么知道它够了?声明一大块内存并保留对它的引用以便GC不会启动是否很简单?客户端应用程序使用这些消息怎么样?是否还需要按照这些严格的标准编写?

另外,我需要一个特殊工具来查看内存吗?到目前为止,我一直在使用SciTech内存分析器.

Tim*_*mwi 9

我发现你所关联的论文相当缺乏:

  • 它假定,并且希望您假设垃圾收集是最终的延迟杀手.他们没有解释他们为什么这么认为,也没有解释他们的系统基本上不是伪装的定制垃圾收集器.
  • 它讨论了垃圾收集中清理的内存量,这是无关紧要的:垃圾收集所需的时间更多地取决于对象数量,而不管它们的大小.
  • 底部的"结果"表与使用.NET垃圾收集器的系统无法进行比较.

当然,这并不意味着他们撒谎而且与垃圾收集无关,但它基本上意味着论文只是试图让人印象深刻,而不会泄露任何有用的东西来构建自己的东西.

  • 大多数JIT没有做足够的优化来与静态编译与配置文件引导优化竞争.使用.net的原因是生成托管代码要便宜得多.做这样的事情并不是很复杂.您预先分配所有资源,然后不运行GC.许多人使用对象池实现这种类型的体系结构. (2认同)

Jon*_*nna 5

从一开始就要注意的一点是,他们说"传统智慧一直在开发低延迟消息传递技术,需要使用非托管C++或汇编语言".特别是,他们谈论的是一种人们常常无法解雇.NET(或Java)解决方案的情况.就此而言,一个相对天真的C++解决方案可能也不会取得成绩.

这里要考虑的另一件事是,他们基本上已经没有那么多被GC取代它了 - 那里有管理对象生命周期的代码,但它是他们自己的代码.

有几种不同的方法可以做到这一点.这是一个.假设我需要在应用程序运行时创建和销毁几个Foo对象.Foo创建由int参数化,因此正常的代码将是:

public class Foo
{
    private readonly int _bar;
    Foo(int bar)
    {
        _bar = bar;
    }
    /* other code that makes this class actually interesting. */
}

public class UsesFoo
{
    public void FooUsedHere(int param)
    {
        Foo baz = new Foo(param)
        //Do something here
        //baz falls out of scope and is liable to GC colleciton
    }
}
Run Code Online (Sandbox Code Playgroud)

一种截然不同的方法是:

public class Foo
{
    private static readonly Foo[] FOO_STORE = new Foo[MOST_POSSIBLY_NEEDED];
    private static Foo FREE;
    static Foo()
    {
        Foo last = FOO_STORE[MOST_POSSIBLY_NEEDED -1] = new Foo();
        int idx = MOST_POSSIBLY_NEEDED - 1;
        while(idx != 0)
        {
            Foo newFoo = FOO_STORE[--idx] = new Foo();
            newFoo._next = FOO_STORE[idx + 1];
        }
        FREE = last._next = FOO_STORE[0];
    }
    private Foo _next;
    //Note _bar is no longer readonly. We lose the advantages
    //as a cost of reusing objects. Even if Foo acts immutable
    //it isn't really.
    private int _bar;
    public static Foo GetFoo(int bar)
    {
        Foo ret = FREE;
        FREE = ret._next;
        return ret;
    }
    public void Release()
    {
        _next = FREE;
        FREE = this;
    }
    /* other code that makes this class actually interesting. */
}

public class UsesFoo
{
    public void FooUsedHere(int param)
    {
        Foo baz = Foo.GetFoo(param)
        //Do something here
        baz.Release();
    }
}
Run Code Online (Sandbox Code Playgroud)

如果你是多线程的话,可以添加更多的复杂功能(虽然在非交互式环境中非常高性能,你可能希望每个线程有一个线程或单独的Foo类存储),如果你不能提前预测MOST_POSSIBLY_NEEDED(最简单的是根据需要创建新的Foo(),但不能为GC释放它们,如果FREE._next为null,则可以通过创建新的Foo在上面的代码中轻松完成.

如果我们允许不安全的代码,我们可以拥有更大的优势,使Foo成为一个结构(因此数组保持堆栈内存的连续区域),_next是指向Foo的指针,GetFoo()返回一个指针.

这是否是这些人实际做的事情,我当然不能说,但上述确实阻止了GC的激活.这只会在非常高的吞吐量条件下更快,如果不是那么让GC做它的东西可能更好(GC确实对你有帮助,尽管有90%的问题将它视为一个大坏事).

还有其他类似的方法可以避免使用GC.在C++中,可以重写new和delete运算符,这允许更改默认创建和销毁行为,并且讨论如何以及为什么这样做可能会让您感兴趣.

实际的一点是,当对象要么保存除了内存之外的资源(例如与数据库的连接),要么在继续使用它们时"学习"(例如XmlNameTables).在这种情况下,池化对象很有用(默认情况下,ADO.NET连接在幕后执行).在这种情况下,虽然简单的队列是要走的路,但内存方面的额外开销并不重要.您还可以放弃锁定争用中的对象(您希望获得性能,锁定争用会比放弃对象更伤害它),我怀疑它会在他们的情况下起作用.