Java - 堆内存 vs 直接内存访问

11
我最近发现了sun.misc.Unsafe类,允许用户以类似于C语言的方式分配、释放和访问内存。我阅读了几篇博客来解决这个问题,例如:
  1. Java堆还是直接内存更快 - 测试结果声称堆更快
  2. Off-heap 内存 vs DirectByteBuffer vs Heap - Off-heap 看起来是最快的
  3. 用于时间序列数据的内存映射文件 - MappedByteBuffer比堆对象更快
文章1)似乎与其他文章相矛盾,我无法理解为什么。DirectMemoryBuffer在底层使用sun.misc.UnsafeMappedByteBuffer也是如此),因此它们也应该像文章1中描述的那样受到JNI调用的影响。此外,在文章2中,Off-heap内存访问类似于文章1中的访问方式,并给出完全相反的结果。
有人能否就如何处理Off-heap内存发表评论,例如何时使用它、是否有显著的好处以及最重要的是,为什么上述类似的主题基于不同的文章给出高度不同的结果?谢谢。

@Hot Licks:好的,我理解了那一点。现在我的进一步问题是——如果我有1000个大小为10字节的对象,并且我调用1000次 Unsafe.putInt(每个对象地址一次),那么这与在10,000字节对象上调用1000次 Unsafe.putInt 并且每次使用不同的偏移量(即0,然后4、8等)有何不同?性能提升在哪里,因为在两种情况下我们都应该精确地穿越JNI界限1000次? - Bober02
根据文章并非如此,这正是令我感到困惑的地方... - Bober02
(请理解,您可以创建基准来证明您想要的任何事情。) - Hot Licks
谢谢,那么使用Unsafe的建议是什么?当分配大块内存时,当使用MemoryMappedFiles时...? - Bober02
这里有一个相当不错的概述:http://www.javacodegeeks.com/2013/12/the-infamous-sun-misc-unsafe-explained.html(特别注意授权检查是多么复杂和缓慢。) - Hot Licks
显示剩余5条评论
1个回答

9

1). 从Java中使用本地内存有其用途,例如当您需要处理大量数据(> 2千兆字节)或想要避免垃圾收集器时。但就延迟而言,直接从JVM访问内存并不比访问堆更快,正如上面展示的那样。实际上,结果是有意义的,因为穿越JVM屏障必须付出代价。这就是使用直接ByteBuffer还是Heap ByteBuffer之间的困境。直接ByteBuffer的速度优势不在于访问速度,而在于能够直接与操作系统的本地I/O操作交互。Peter Lawrey讨论的另一个很好的例子是在处理时间序列时使用内存映射文件。

来源:http://mentablog.soliveirajr.com/2012/11/which-one-is-faster-java-heap-or-native-memory/

2). 通过Unsafe的离堆内存速度极快,为330/11200百万/秒。对于所有其他类型的分配,性能要么适合读取,要么适合写入,没有一种分配方式适合同时进行读写。特别是关于ByteBuffer, 它的性能很差,我敢肯定在看到这样的数字后您不会再使用它。DirectBytebuffer在读取速度方面很慢,我不确定为什么会这么慢。因此,如果内存读写成为系统瓶颈,那么离堆是解决问题的方法,记住这是高速公路,所以要小心驾驶。

来源:http://www.javacodegeeks.com/2013/08/which-memory-is-faster-heap-or-bytebuffer-or-direct.html


从第二篇文章中:“对于内存分配测试,我将使用13个字节的消息,并将其分解为...”。看起来写入的数据很小,没有大块数据,并且在调用Unsafe方法时不断交叉。所以我仍然不知道测试之间的差异在哪里,存在什么差异。 - Bober02
是的,我觉得这篇文章的不足之处就在于此。关于内存分配,他只测试了写入/读取性能,完全忽略了内存消耗/分配速度。涉及到这方面的内容(他从未关注过)可能会解释为什么调用Unsafe方法时它会被不断地穿越。 - MGot90
那么,另一篇文章也忽略了测试读写访问权限时的分配/释放事实?也就是说,纯粹测试读写访问权限,但结果却大不相同... - Bober02
需要注意的是,“离堆”和堆内存的性能没有区别——它们是完全相同的内存,而它是堆并不会增加任何开销。 - Hot Licks

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