技术开发 频道

Java运行时如何使用本机内存

  NIO

  Java 1.4 中添加的新 I/O (NIO) 类引入了一种基于通道和缓冲区来执行 I/O 的新方式。就像 Java 堆上的内存支持 I/O 缓冲区一样,NIO 添加了对直接 ByteBuffer 的支持(使用 java.nio.ByteBuffer.allocateDirect() 方法进行分配), ByteBuffer 受本机内存而不是 Java 堆支持。直接 ByteBuffer 可以直接传递到本机操作系统库函数,以执行 I/O — 这使这些函数在一些场景中要快得多,因为它们可以避免在 Java 堆与本机堆之间复制数据。

  对于在何处存储直接 ByteBuffer 数据,很容易产生混淆。应用程序仍然在 Java 堆上使用一个对象来编排 I/O 操作,但持有该数据的缓冲区将保存在本机内存中,Java 堆对象仅包含对本机堆缓冲区的引用。非直接 ByteBuffer 将其数据保存在 Java 堆上的 byte[] 数组中。下图展示了直接与非直接 ByteBuffer 对象之间的区别:

  直接与非直接 java.nio.ByteBuffer 的内存拓扑结构  

  直接 ByteBuffer 对象会自动清理本机缓冲区,但这个过程只能作为 Java 堆 GC 的一部分来执行,因此它们不会自动响应施加在本机堆上的压力。GC 仅在 Java 堆被填满,以至于无法为堆分配请求提供服务时发生,或者在 Java 应用程序中显式请求它发生(不建议采用这种方式,因为这可能导致性能问题)。

  发生垃圾收集的情形可能是,本机堆被填满,并且一个或多个直接 ByteBuffers 适合于垃圾收集(并且可以被释放来腾出本机堆的空间),但 Java 堆几乎总是空的,所以不会发生垃圾收集。

  线程

  应用程序中的每个线程都需要内存来存储器堆栈(用于在调用函数时持有局部变量并维护状态的内存区域)。每个 Java 线程都需要堆栈空间来运行。根据实现的不同,Java 线程可以分为本机线程和 Java 堆栈。除了堆栈空间,每个线程还需要为线程本地存储(thread-local storage)和内部数据结构提供一些本机内存。

  堆栈大小因 Java 实现和架构的不同而不同。一些实现支持为 Java 线程指定堆栈大小,其范围通常在 256KB 到 756KB 之间。

  尽管每个线程使用的内存量非常小,但对于拥有数百个线程的应用程序来说,线程堆栈的总内存使用量可能非常大。如果运行的应用程序的线程数量比可用于处理它们的处理器数量多,效率通常很低,并且可能导致糟糕的性能和更高的内存占用。

0
相关文章