代码之家  ›  专栏  ›  技术社区  ›  Carlos

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

  •  16
  • Carlos  · 技术社区  · 14 年前

    这里有一个有趣的例子 article 我在网上找到的。

    它讨论了这家公司如何能够在托管环境中解析大量财务数据,基本上是通过对象重用和避免字符串之类的不可变数据。然后他们继续展示 他们的程序不做任何GC

    这真是令人印象深刻,我想知道这里还有没有人有更多的 如何做到这一点。首先,我想知道当消息中的一些数据显然是字符串时,如何避免使用字符串,并且无论客户端应用程序查看消息时,都希望传递这些字符串?另外,在启动阶段你会分配什么?你怎么知道已经够了?声明一大块内存并保留对它的引用,这样GC就不会启动了,这简单吗? 无论哪个客户端应用程序正在使用这些消息呢?是否也需要按照这些严格的标准来写?

    另外,我需要一个特殊的工具来查看内存吗?到目前为止,我一直在使用SciTech memory profiler。

    5 回复  |  直到 10 年前
        1
  •  9
  •   Timwi    14 年前

    • 它假设,并希望您假设,垃圾收集是最终的延迟杀手。他们没有解释他们为什么这样想,也没有解释他们以什么方式这样想 他们的
    • 它谈到了 内存量 在垃圾收集中清理,这是不相关的:垃圾收集所花费的时间更多地取决于 ,无论大小。

    当然,这并不意味着他们在撒谎,也与垃圾收集无关,但这基本上意味着这篇论文只是试图让人印象深刻,而没有透露任何有用的东西,你可以用来建立自己的。

        2
  •  6
  •   Jon Hanna    14 年前

    从一开始就要注意的是,他们说“传统的智慧一直在开发低延时消息技术,需要使用非托管C++或汇编语言”。特别是,他们正在讨论一种情况,在这种情况下,人们通常会立即忽略.NET(或Java)解决方案。在这方面,相对的C++解决方案可能也不会成为等级。

    有几种不同的方法可以替代。这里有一个。假设我需要在应用程序运行时创建和销毁几个Foo对象。Foo creation由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
        }
    }
    

    另一种截然不同的方法是:

    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();
        }
    }
    

    如果我们允许不安全的代码,那么Foo是一个struct(因此数组拥有一个连续的堆栈内存区域),接下来是一个指向Foo的指针,GetFoo()返回一个指针,这将有更大的优势。

    我当然不能说这些人到底在做什么,但以上确实阻止了GC的激活。这只会在非常高的吞吐量条件下更快,如果不这样做,那么让GC完成它的工作可能更好(GC确实可以帮助您,尽管有90%的问题认为它是一个大坏蛋)。

    一个实际的解决方法是,对象要么持有除内存以外的昂贵资源(例如与数据库的连接),要么在继续使用时“学习”(例如XmlNameTables)。在这种情况下,池对象很有用(ADO.NET 默认情况下,连接是在幕后进行的)。在这种情况下,尽管简单的队列是可行的,但内存方面的额外开销并不重要。您还可以在锁争用上放弃对象(您希望获得性能,而锁争用对它的影响比放弃对象更大),我怀疑这在他们的情况下是否有效。

        3
  •  2
  •   Pedro Rodrigues    14 年前

    不变的

    free lists 可变字符串。

        4
  •  2
  •   Community CDub    4 年前

    我在一个叫CEP的产品上工作了一段时间 StreamBase

    这似乎有悖常理,但他们的产品速度极快。

    from their site

    StreamBase通过两种方式避免垃圾收集:不使用对象,只使用我们需要的最小对象集。

    最后,我们使用衡量每元组垃圾收集的基准测试工具对其进行内部测试。为了实现我们的高速,我们尝试消除所有的每元组垃圾收集,通常取得了很好的效果。

        5
  •  0
  •   Steven    14 年前

    在99%的时间里,当你试图做到这一点时,你会浪费老板的钱。这篇文章描述了一个绝对极端的场景,在那里他们需要最后一次性能下降。正如您在文章中所读到的,当您尝试不使用GC时,.netframework中有很多部分是无法使用的。BCL的一些最基本的部分使用内存分配(或者论文称之为“产生垃圾”)。你需要找到绕过这些方法的方法。即使您需要绝对快速的应用程序,在尝试走无GC路线之前,最好先尝试构建一个可以扩展的应用程序/体系结构(使用多台机器)。他们使用no-GC路由的唯一原因是他们需要一个绝对低的延迟。IMO,当您需要绝对速度,但不关心绝对最小响应时间时,很难证明无GC架构是合理的。除此之外,如果您尝试构建一个无GC的客户端应用程序(如Windows窗体或WPF应用程序);算了吧,那些表示框架不断地创建新对象。

    但如果你真的想要这个,其实很简单。下面是一个简单的方法:

    • 找出.NETAPI的哪些部分不能使用(您可以编写一个工具,使用 introspection engine
    • 编写一个程序来验证您或您的开发人员编写的代码,以确保他们不会直接分配或使用“禁止的”.NET方法,使用在上一点中创建的安全列表(FxCop是一个很好的工具)。
    • 创建在启动时初始化的对象池。程序的其余部分可以重用现有对象,这样它们就不必做任何操作 new 行动。
    • 如果您需要操作字符串,请为此使用字节数组,并将字节数组存储在池中(WCF也使用此技术)。您必须创建一个允许操作这些字节数组的API。

    祝你好运