Java本地内存比堆内存更快吗?

5
我正在寻找帮助我的内存密集型应用程序的选项,因此我了解到Terracotta的BigMemory。据我所知,他们利用非垃圾回收的离堆“本机内存”,显然由于序列化/反序列化问题,这比堆存储慢约10倍。在了解BigMemory之前,我从未听说过“本机内存”除了正常的JNI。虽然BigMemory是一个值得进一步考虑的有趣选择,但如果可以绕过序列化问题,我对使用本机内存可以实现什么感到着迷。
当没有序列化问题时,Java本机内存(我认为这包括ByteBuffer对象?)是否比传统堆内存更快(例如,如果我将其与巨大的byte[]进行比较)?或者垃圾收集等方面的不确定性使这个问题无法回答?我知道“测量它”的答案在这里很常见,但我担心我不能设置一个代表性的测试,因为我还不知道Java中的本机内存是如何工作的。

现代即时编译器在可能的情况下会将堆使用转换为本地栈使用。而且,如果不强制在字节码解释器中运行它,并且说“这是Java堆,这是本地堆”,那么甚至测试它都会非常困难。 - Affe
3个回答

4

直接内存在执行IO时更快,因为它避免了数据的一次复制。但是对于95%的应用程序,您不会注意到差异。

您可以将数据存储在直接内存中,但它不会比存储数据POJOs更快。(或者不像POJOs那样安全、可读和易于维护)如果您担心GC,请尝试提前创建对象(必须是可变的)并重复使用它们而不丢弃它们。如果您不丢弃对象,则没有要收集的内容。


当没有序列化问题时(例如,如果我将其与一个巨大的byte[]进行比较),Java本机内存是否比传统堆内存更快(我认为这涉及ByteBuffer对象?)

如果使用非字节(如int),则直接内存可能比使用byte[]更快,因为它可以读/写整个四个字节而不将数据转换为字节。但是,它比使用POJOs慢,因为它必须检查每个访问的边界。

或者垃圾回收等变数使这个问题无法回答?

速度与GC无关。 GC只在创建或丢弃对象时才有影响。

顺便说一下:如果您最小化丢弃的对象数量并增加Eden大小,则可以防止发生甚至较小的收集,例如整整一天。


尝试提前创建对象(必须是可变的),并重复使用它们而不丢弃它们。在Java中,最好的做法之一是不要有对象池,为什么你会这么说? - Pih
1
@Peter Lawrey GC 真的完全不需要考虑吗?在堆上执行所有操作需要更大的堆,我相信这需要 GC 更多的管理。使用本地内存意味着 GC 可以管理一个小得多的堆;除非我弄错了。 - Michael McGowan
@pih 我不确定我会说不使用对象池是最佳实践,但如果对象在使用之间没有得到适当的清理和重置,它们确实会带来一些错误的风险。我认为应该根据情况使用它们,并且通常可以重复使用大的IO缓冲区等,而不是重新分配内存并按需创建它们。 - squawknull
@Michael 在分代垃圾收集中,垃圾收集器会一次性回收一个代。一旦对象移动到老年代空间,它们就不会影响年轻一代。(实际上使用了另一个GC) - Peter Lawrey
1
@sqawknull,我同意重用对象比丢弃对象更容易出错,应根据您的系统适当使用。这可能根本不需要,或者它可能是您系统的核心。我想明确说明这是一种选择,使用直接缓冲区并不是唯一的选择。(实际上,直接缓冲区可能更容易出错,因为您没有类型检查) - Peter Lawrey
显示剩余5条评论

2
BigMemory的重点不在于本地内存更快,而是减少垃圾收集器需要跟踪内存引用并清理它的开销。随着堆大小增加,GC间隔和CPU使用率也会增加。根据情况不同,这可能会创建一种“玻璃天花板”,其中Java堆变得如此之大,以至于GC成为一个占用巨大处理器资源的程序。此外,许多GC算法需要某种级别的锁定,这意味着在GC引用跟踪算法的该部分完成之前,没有人可以做任何事情,虽然许多JVM已经在处理这方面有了很大进展。在我们的应用服务器和JVM上工作时,我们发现“玻璃天花板”约为1.5 GB。如果我们尝试将堆配置得比这更大,GC例程开始占用超过总CPU时间的50%,因此这是一个非常真实的代价。我们通过各种JVM供应商提供的GC分析方法来确定这一点。
另一方面,BigMemory采取了更手动的内存管理方法。它减少了开销,并使我们回到了像C中那样需要自己清理内存的状态,尽管这是一种更简单的方法类似于HashMap。这基本上消除了传统垃圾收集例程的需要,因此我们消除了这种开销。我认为Terracotta使用本地内存通过ByteBuffer,因为这是摆脱Java垃圾收集器的简单方法。
以下白皮书提供了关于BigMemory架构和GC开销的一些有用信息:http://www.terracotta.org/resources/whitepapers/bigmemory-whitepaper

请注意,这个“玻璃天花板”值高度依赖于应用程序。我们大多使用50到80 GiB作为堆大小,并且与20到30 GiB的较小设置相比,没有看到GC的任何显着开销。 - kap
请注意,这个“玻璃天花板”值在很大程度上取决于应用程序。我们通常使用50到80 GiB作为堆大小,并且与20到30 GiB的较小设置相比,没有看到GC方面的任何显著开销。 - kap

1
我对如果可以绕过序列化问题,利用本地内存能够实现什么很感兴趣。
我认为你的问题是基于错误的假设。据我所知,无法绕过他们在这里谈论的序列化问题。你唯一能做的就是简化放入BigMemory中的对象,并使用自定义序列化/反序列化代码来减少开销。
虽然基准测试可能会给您一个大致的开销概念,但实际开销将非常应用程序特定。我的建议是:
- 只有在确实需要时才采用此方法。(您将把应用程序与特定的实现技术联系起来。) - 如果涉及的数据尚未作为缓存进行管理,则准备好对应用程序进行一些侵入性更改。 - 准备花费一些时间来(重新)调整缓存代码,以在BigMemory中获得良好的性能。 - 如果您的数据结构很复杂,请期望相应比例的运行时开销和调整工作量。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接