在 Java 中使用直接内存的目的是什么?

nin*_*ris 5 java jvm

从 java 1.4 开始引入直接内存。新的 I/O (NIO) 类引入了一种基于通道和缓冲区执行 I/O 的新方法。NIO 添加了对直接 ByteBuffers 的支持,它可以直接传递到本机内存而不是 Java 堆。在某些情况下使它们显着更快,因为它们可以避免在 Java 堆和本机堆之间复制数据。

我一直不明白我们为什么要使用直接内存。有人可以帮忙举个例子吗?

Pet*_*rey 12

我一直不明白我们为什么要使用直接内存。有人可以帮忙举个例子吗?

所有系统调用(例如读写套接字和文件)仅使用本机内存。他们不能使用堆。这意味着虽然您可以从堆复制到本机内存/从本机内存复制,但避免这种复制可以提高效率。

我们使用堆外/本机内存来存储我们的大部分数据,这具有许多优点。

  • 它可以大于堆大小。
  • 它可以大于主内存。
  • 它可以在 JVM 之间共享。即多个 JVM 的一份副本。
  • 它可以在 JVM 甚至机器重新启动时持久化和保留。
  • 它对 GC 暂停时间几乎没有影响。
  • 根据使用情况,它可以更快

它没有被更多使用的原因是它更难使它既高效又像普通 Java 对象一样工作。出于这个原因,我们有诸如Chronicle Map 之类的库,它充当ConcurrentMap但使用堆外内存,以及Chronicle Queue,它是进程之间的日志、记录器和持久 IPC。


mju*_*rez 6

JVM 依靠垃圾收集的概念来回收不再使用的内存。这允许 JVM 语言开发人员(例如,Java、Scala 等)不必担心内存分配和释放。您只需请求内存,让 JVM 担心它何时会被回收或垃圾收集。

虽然这非常方便,但它带来了单独线程的额外开销,消耗 CPU 并且必须不断地通过 JVM 堆,回收不再可访问的对象。有很多关于这个主题的书,但如果你想阅读更多关于 JVM 垃圾收集的信息,那里有大量的参考资料,但这个是不错的:https : //dzone.com/articles/understanding-the- java-内存模型和垃圾袋

无论如何,如果在您的应用程序中,您知道将要进行大量复制、更新对象和值,您可以选择自己处理这些对象及其内存消耗。因此,无论这些对象有多少搅动,这些对象永远不会在堆中移动,它们永远不会被垃圾收集,因此不会影响 JVM 中的垃圾收集。这个答案有更多细节:https : //stackoverflow.com/a/6091680/236528

来自官方 Javadoc:

直接与非直接缓冲区

字节缓冲区可以是直接的,也可以是非直接的。给定一个直接字节缓冲区,Java 虚拟机将尽最大努力直接在其上执行本机 I/O 操作。也就是说,它将尝试避免在每次调用底层操作系统的本机 I/O 操作之前(或之后)将缓冲区的内容复制到(或从)中间缓冲区。

直接字节缓冲器可以通过调用来创建allocateDirect 这个类的工厂方法。此方法返回的缓冲区通常比非直接缓冲区具有更高的分配和解除分配成本。直接缓冲区的内容可能驻留在正常的垃圾收集堆之外,因此它们对应用程序内存占用的影响可能并不明显。因此,建议将直接缓冲区主要分配给受底层系统本地 I/O 操作影响的大型、长期存在的缓冲区。一般而言,最好仅在程序性能产生可衡量的增益时才分配直接缓冲区。

https://download.java.net/java/early_access/jdk11/docs/api/java.base/java/nio/ByteBuffer.html

  • 引用的部分只说直接缓冲区“可能驻留在正常的垃圾收集堆之外”,但从未声称这对垃圾收集器的性能产生了积极影响。事实上,它表示它们可能有“更高的分配和重新分配成本”。由于任何严肃的 JVM 都知道“byte[]”数组中没有对象引用,因此在垃圾收集方面,直接缓冲区和非直接缓冲区之间没有显着差异。当本机代码想要访问内存时,差异就会发挥作用,这是一个完全不同的主题。 (4认同)