我有一个在低延迟环境中运行的 (java) 应用程序,它通常在 ~600 微秒 (+/- 100) 内处理指令。当然,随着我们进一步进入微秒级空间,您看到的延迟成本发生了变化,现在我们注意到 2/3 的时间花在了 2 个核心域对象的分配上。
基准测试已经将有问题的代码部分从字面上从现有引用中分离出对象的构造,即基本上是大量引用(每个类中约 15 个)和几个新的列表,尽管请参阅下面的注释这里测量的是什么。
每个人始终需要大约 100 微秒,这对我来说是莫名其妙的,我正在努力找出原因。一个快速基准测试表明,一个类似大小的充满字符串的对象需要大约 2-3 微秒才能更新,显然这种基准测试充满了困难,但认为它可能作为基准有用。
这里有2个问题
- 如何调查此类行为?
- 分配缓慢的原因是什么?
请注意,涉及的硬件是 Sun X4600s 上的 Solaris 10 x86,具有 8 个双核 opterons @ 3.2GHz
我们看过的东西包括
- 检查 PrintTLAB 统计数据,显示 v 几个缓慢的分配,因此那里应该没有争用。
- PrintCompilation 表明其中一段代码不是 JIT 友好的,尽管 Solaris 在这里似乎有一些不寻常的行为(即与现代 linux 相比,现在没有与 solaris10 类似的老式 linux 可以使用)
- LogCompilation...至少可以说有点难以解析,所以这是一项正在进行的工作,目前还没有什么明显的
- JVM 版本...在 6u6 和 6u14 上保持一致,尚未尝试 6u18 或最新的 7
感谢所有想法
各种帖子的评论摘要,试图让事情更清楚
- 我正在衡量的成本是创建对象的总成本,该对象是通过 Builder(如 these 之一)构建的,其私有(private)构造函数调用 new ArrayList 几次以及设置对现有对象的引用。衡量的成本包括设置构建器和将构建器转换为域对象的成本
- 编译(按热点)有显着影响,但仍然相对较慢(在这种情况下编译将其从 100 微秒降至约 60 微秒)
- 在我的原始基准测试中(通过热点)编译将分配时间从约 2 微秒减少到约 300 纳秒
- 延迟不随年轻代收集算法(ParNew 或并行清除)而变化
最佳答案
由于您的问题更多是关于如何着手调查问题,而不是“我的问题是什么”,我将坚持使用一些工具进行尝试。
BTrace 是一个非常有用的工具,可以帮助您更好地了解正在发生的事情和时间。 .它类似于 DTrace,但它是一个纯 Java 工具。关于这一点,我假设您知道 DTrace,如果不了解,那即使不晦涩也很有用。这些将使您能够了解 JVM 和操作系统中正在发生的事情以及发生的时间。
哦,还有一件事要在您的原始帖子中澄清。你在运行什么收集器?我假设您正在使用像 CMS 这样的低暂停收集器来解决高延迟问题。如果是这样,您是否尝试过任何调整?
关于java - 分配延迟似乎很高,为什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1751588/