这是我在网上找到的一个有趣的article。
它讨论了该公司如何能够在托管环境中解析大量财务数据,主要是通过对象重用和避免使用字符串等不可变量。然后他们继续说明他们的程序在连续操作阶段没有做任何GC 。
这非常令人印象深刻,我想知道其他人是否有更多详细指南如何执行此操作。首先,我想知道如何避免使用字符串,当消息中的某些数据是字符串时,无论客户端应用程序正在查看消息,都希望传递这些字符串?另外,你在启动阶段分配了什么?你怎么知道它够了?声称拥有大量内存并保留对它的引用以便GC不会启动,这是否很简单? 客户端应用程序使用这些消息怎么样?是否还需要根据这些严格的标准编写?
另外,我需要一个特殊工具来查看内存吗?到目前为止,我一直在使用SciTech内存分析器。
答案 0 :(得分:9)
我发现你所关联的论文相当缺乏:
当然,这并不意味着他们撒谎而且与垃圾收集无关,但它基本上意味着报纸只是试图听起来令人印象深刻而没有真正泄露任何有用的东西,你可以用来建立自己的
答案 1 :(得分: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
}
}
一种截然不同的方法是:
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类存储),如果您无法预测MOST_POSSIBLY_NEEDED事先(最简单的是根据需要创建新的Foo(),但不要为GC发布它们,如果FREE._next为null,可以通过创建新的Foo在上面的代码中轻松完成。)
如果我们允许不安全的代码,我们可以拥有更大的优势,让Foo成为一个结构(因此数组保存一个连续的堆栈内存区域),_next是指向Foo的指针,而GetFoo()返回一个指针。 / p>
这是否是这些人实际做的事情,我当然不能说,但上述确实阻止了GC的激活。这只会在非常高的吞吐量条件下更快,如果不是那么让GC做它的东西可能更好(GC确实对你有帮助,尽管有90%的问题将它视为大坏事。)
还有其他类似的方法可以避免使用GC。在C ++中,可以覆盖new和delete运算符,这样可以更改默认的创建和销毁行为,并且讨论如何以及为什么这样做可能会让您感兴趣。
实际的一点是,当对象要么保留除了内存以外的资源(例如与数据库的连接)或者在继续使用它们时“学习”(例如XmlNameTables)。在这种情况下,池化对象很有用(默认情况下,ADO.NET连接在幕后执行)。在这种情况下,虽然简单的队列是要走的路,但内存方面的额外开销并不重要。您也可以放弃锁定争用中的对象(您希望获得性能,锁定争用会比放弃对象更容易受到伤害),我怀疑它会在他们的情况下起作用。
答案 2 :(得分:2)
据我所知,文章并未说明他们不使用字符串。它们不使用不可变字符串。不可变字符串的问题在于,当您进行解析时,生成的大多数字符串都只是丢弃字符串。
我猜他们正在使用某种预分配与free lists可变字符串相结合。
答案 3 :(得分:2)
我使用名为StreamBase的CEP产品工作了一段时间。他们的一位工程师告诉我他们正在将他们的C ++代码迁移到Java,因为他们通过完全避免使用GC来获得更好的性能,更少的错误和更好的JVM可移植性。我想这些论点也适用于CLR。
这看似违反直觉,但他们的产品非常快。
以下是一些信息from their site:
StreamBase以两种方式避免垃圾收集:不使用对象,只使用我们需要的最小对象集。
首先,我们通过使用Java原始类型(Boolean,byte,int,double和long)来表示我们要处理的数据,从而避免使用对象。每个StreamBase数据类型由一个或多个基本类型表示。通过仅操纵基元类型,我们可以有效地将数据存储在堆栈或数组分配的内存区域中。然后我们可以使用并行数组或方法调用等技术来有效地传递数据。
其次,当我们使用对象时,我们会小心它们的创建和破坏。我们倾向于汇集对象而不是释放它们以进行垃圾收集。我们尝试管理对象生命周期,使得对象可以被年轻一代的垃圾收集器捕获,或者永远保存。
最后,我们使用测量每元组垃圾收集的基准测试工具在内部对其进行测试。为了实现我们的高速度,我们尝试消除所有的元组垃圾收集,通常取得了很好的成功。
答案 4 :(得分:0)
99%的时间,当你试图实现这一目标时,你会浪费你的老板的钱。本文描述了一个绝对极端的场景,他们需要最后一滴性能。正如您可以在文章中看到的那样,.NET框架的很多部分在尝试无GC时无法使用。 BCL的一些最基本的部分使用内存分配(或“生成垃圾”,正如论文所称)。您需要找到解决这些方法的方法。即使你需要绝对超快的应用程序,你最好先尝试构建一个可以扩展(使用多台机器)的应用程序/体系结构,然后再尝试使用无GC路由。他们使用无GC路线的唯一原因是他们需要绝对的低延迟。 IMO,当你需要绝对速度,但不关心绝对最小响应时间时,很难证明无GC架构的合理性。除此之外,如果您尝试构建一个不含GC的客户端应用程序(例如Windows Forms或WPF App);忘掉它,那些演示框架不断创造新的对象。
但如果你真的想要这个,那实际上很简单。这是一个简单的如何:
new
操作。