# JVM
- 直接内存
JDK
版本:1.8
# 1、直接内存(Direct Memory
)
直接内存(Direct Memory
)并不是虚拟机运行时数据区的一部分,也不是《Java
虚拟机规范》中定义的内存区域。但是这部分内存也会被频繁地使用,而且也会导致OutOfMemeory
异常。
在JDK 1.4
版本中引入了NIO
类,引入了一种基于通道(Channel
)于缓冲区(Buffer
)的I/O
方式,它可以直接调用Native
函数分配堆外内存空间,然后通过存储在堆中的DirectByteBuffer
对象作为这块空间的引用以此来对其进行操作。这样就避开了JVM
于操作系统之间的内存屏障,减少了在Java
堆与Native
堆中进行来回复制的额外开销,在某些场景中可以显著提高性能。同时访问直接内存的速度也会优于Java
堆,即读写性能高。
本机直接内存的分配是不会受到JVM
堆内存大小的限制,但是它还是会受到本机总内存(包括物理内存,SWAP
分区或者分页文件)的大小以及处理器寻址空间的限制。一般在配置虚拟机参数时,会根据实际内存设置-Xmx
等参数信息,但经常忽略掉直接内存,使得各个内存区域总和大于物理内存限制(包括物理和操作系统级的限制),从而导致动态进行扩展时出现OutOfMemory
异常。
也正是因为直接内存不是虚拟机运行时数据区的一部分,直接内存不受JVM
内存回收管理,所以其分配回收成本也较高。直接内存大小可通过MaxDirectMemorySize
选项进行配置,如果不进行指定默认与堆空间的-Xmx
参数值保持一致。
# 2、直接内存与非直接内存差异
使用非直接内存进行IO
读写文件时,需要与物理磁盘进行交互。在执行读写操作时,JVM
与OS
直接存在内存屏障,所以进行数据的交互时就会进行来回复制。换而言之就是需要由用户态切换到内核态,内核态中需要同时维护两份重复数据,这样带来的额外性能开销是非常大的。
如下为非直接内存JVM
与OS
之间的数据交互图:
使用直接内存进行IO
读写文件时,直接调用Native
函数分配堆外内存空间,并且这部分内存可以通过存储在堆中的DirectByteBuffer
对象对其进行操作,避免了内存屏障同时也只需要维护一份数据。避免了不必要的性能开销。
如下为直接内存JVM
与OS
之间的数据交互图:
GitHub源码地址:https://github.com/kapbc/Java-Kapcb/tree/master/src/main/java/com/kapcb/ccc/jvm
备注:此文为笔者学习
JVM
的笔记,鉴于本人技术有限,文中难免出现一些错误,感谢大家批评指正。
更多Java技术笔记可扫码关注下方微信公众号。