浅谈Java堆内内存、堆外内存、直接内存、用户空间和内核空间

我们都知道Java中大多数的对象都存在于堆内存中,那什么是堆外内存、直接内存?它们又分别用来做什么?分布在用户空间还是内核空间?

首先,有个前置知识点,所谓的Java程序,其实可以理解为是用C/C++写的运行于用户空间的进程。在Java程序运行时,大多数的对象都位于堆内存,这个内存是受控于JVM的。而堆外内存,广义上是包括除了JVM管理的数据区以外的内存(包括黄色部分 + 粉色部分),狭义上则是指粉色部分,Java进程的内存大小近似等于Java堆内内存加上堆外内存的大小,如图

而Java1.8的 Meta Sapce 之所以不设置大小的时候,能直接受限于物理内存,就是因为它位于堆外内存,不受JVM空间大小的限制。

接下来说说直接内存(native memory)。在Java中直接内存一般指的是使用Unsafe和NIO包下ByteBuffer来创建内存区域。  它脱离了JVM的管理,分布于堆外,其实直接内存可以近似等于堆外内存(粉色部分)。直接内存的回收并不会被GC自动回收,而是需要做进一步的手动清理,一般配合虚引用完成。

早期的计算机,所有进程都可以直接调用系统底层资源和硬件设备,但是经常会出现一系列的不安全问题。后来为了避免这种问题的出现,操作系统对进程做了隔离,分成了用户模式和内核模式,一些容易发生安全问题的操作都被限制在只有内核模式下才可以执行,例如 I/O 操作,修改基址寄存器内容等。而用户模式下的进程空间则属于用户空间,内核模式下的空间则属于内核空间。

再来说说关于直接内存零拷贝的问题。在Java中BIO模式下,如果程序需要读取硬盘或者远程数据,会经过3次复制的过程

 这里有个问题,为什么需要将数据拷贝到堆外内存,再拷贝到堆内,而不直接拷贝到堆内?因为堆内存是手GC管理的,而在拷贝需要提供对应的目的地址,而拷贝中可能会出现堆内存中有垃圾被清理回收而导致的内存地址发生变化,可能造成目的地址也发生变化,所以需要先拷贝到堆外,再拷贝至堆内。

而用NIO开辟直接内存的方式,则可以在堆外内存开辟位置,少了堆外到堆内的拷贝过程。同时可以利用mmap内存映射方式将堆外的部分内存和内核空间的部分内核进行映射(可以看成使用了同一个物理内存地址),这样又可以少了内核缓冲区到堆外内存拷贝的过程,这就是NIO目前用的比较多的直接内存零拷贝方式。当然零拷贝还有其他方法,就不在这里说明了。

总结能得到以下两个问题答案:

问题1:堆外内存属于用户空间还是内核空间?用户空间

问题2:直接内存为什么效率比堆内内存高?减少了拷贝的次数

  • 18
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值