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

cyej8jka  于 9个月前  发布在  Java
关注(0)|答案(4)|浏览(126)

自Java 1.4开始引入直接内存。新的I/O(NIO)类引入了一种基于通道和缓冲区执行I/O的新方法。NIO添加了对直接ByteBuffers的支持,可以直接将其传递到本机内存而不是Java堆。在某些情况下,由于它们可以避免在Java堆和本机堆之间复制数据,因此使它们的速度明显更快。
我一直不明白为什么我们要用直接记忆。谁能帮我给予举个例子吗?

gtlvzcf8

gtlvzcf81#

我一直不明白为什么我们要用直接记忆。谁能帮我给予举个例子吗?
所有的系统调用,比如阅读和写套接字和文件,都只使用本机内存。它们不能使用堆。这意味着虽然你可以从堆复制到本机内存/从本机内存复制,但避免这种复制可以提高效率。
我们使用堆外/本机内存来存储大部分数据,这有很多优点。

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

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

fhg3lkii

fhg3lkii2#

JVM依赖于垃圾回收的概念来回收不再使用的内存。这使得JVM语言开发人员(例如Java,Scala等)不必担心内存分配和释放。您只需请求内存,让JVM担心何时回收或垃圾回收。
虽然这是非常方便的,但它带来了单独线程的额外开销,消耗CPU,并且必须不断地遍历JVM堆,回收不再可访问的对象。有关于这个主题的整本书,但如果你想阅读更多关于JVM垃圾收集的内容,有很多参考资料,但这一个很不错:https://dzone.com/articles/understanding-the-java-memory-model-and-the-garbag
无论如何,如果在你的应用程序中,你知道你要做大量的复制,更新对象和值,你可以选择自己处理这些对象和它们的内存消耗。所以,不管这些对象中有多少扰动,这些对象永远不会在堆中移动,它们永远不会被垃圾收集,因此,不会影响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

d4so4syb

d4so4syb3#

在Java中与直接内存交互的主要机制是通过使用java.nio包,特别是ByteBuffer类。Java NIO使用这种内存类型以更有效的方式将数据写入网络或磁盘。直接内存通常用于需要与本机代码或I/O操作直接交互的场景。
常见的用例包括网络、文件I/O和使用本机库。特别是ByteBuffer类,通常用于与直接内存交互。

// Allocating direct memory  ByteBuffer directBuffer =
  ByteBuffer.allocateDirect(1024);

字符串
一个常见的用例是处理大文件,例如从文件中阅读或向文件写入大量数据。直接内存可以在这种情况下提高性能。
直接内存是指不受Java虚拟机(JVM)堆控制的内存,通常由操作系统管理。因此,它不会增加垃圾收集开销。这在频繁的垃圾收集暂停是一个问题的情况下可能是有益的。
您可以使用下面的示例代码来使用直接内存并快速读取大文件

FileChannel inChannel = new FileInputStream(fileName).getChannel();
ByteBuffer buffer = ByteBuffer.allocateDirect(CAPACITY);

while(inChannel.read(buffer) > 0)
    buffer.clear(); // do something with the data and clear/compact it.

inChannel.close();

lnxxn5zx

lnxxn5zx4#

还有一个原因没有人提到:您可以在JVM之间高效地共享数据(显然是在同一台机器上运行时)。
由于继承和不可更改的架构,2个JVM被占用在同一个机器上,并且需要在它们之间发送大量数据-最初通过http端点。我能够通过传递直接内存块的内存地址而不是实际数据来显着提高性能:

  • 串行化/序列化开销消失
  • 数据量太大,无法通过http端点传入一个调用,因此避免了多个调用

相关问题