我们可以明显看到directBuffer速度比IO读取快很多,那么究竟是怎么实现的
我们可以分别给出两张图进行解释:
- JVM正常读取

文章插图
- 直接内存读取

文章插图
我们由上图可以得知:
- JVM正常读取需要先复制一份经过系统内存缓冲区,然后再复制一份才能进入到java文件中
- DirectMemory可以同时在系统内存和java堆内存中使用,我们只需要传入数据到直接内存中就可以直接读取调用
package cn.itcast.jvm.t1.direct;import java.nio.ByteBuffer;import java.util.ArrayList;import java.util.List;/** * 演示直接内存溢出 */public class Demo1_10 {static int _100Mb = 1024 * 1024 * 100;public static void main(String[] args) {List<ByteBuffer> list = new ArrayList<>();int i = 0;try {while (true) {// 这里设置一个大小为100mb的直接内存ByteBuffer byteBuffer = ByteBuffer.allocateDirect(_100Mb);list.add(byteBuffer);i++;}} finally {System.out.println(i);}// 方法区是jvm规范,jdk6 中对方法区的实现称为永久代//jdk8 对方法区的实现称为元空间}}
直接内存释放原理我们目前所使用的直接内存是DirectMemory:package cn.itcast.jvm.t1.direct;import java.io.IOException;import java.nio.ByteBuffer;/** * 我们查看内存管理需要到任务管理器里查看 , 因为该内存属于系统内存,不再属于jvm */public class Demo1_26 {static int _1Gb = 1024 * 1024 * 1024;// 我们使用debug模式调试public static void main(String[] args) throws IOException {// 我们使用byteBuffer来调取1G的内存使用// 我们开启项目后会看到一个内存为1G的java项目ByteBuffer byteBuffer = ByteBuffer.allocateDirect(_1Gb);System.out.println("分配完毕...");// 输入空格后开始进行系统的垃圾回收,这时byteBuffer被回收,我们会注意到内存为1G的项目结束System.in.read();System.out.println("开始释放...");byteBuffer = null;System.gc();System.in.read();}}
但是我们需要注意的是我们的jvm的回收功能对系统内存是没有管辖权力的所以回收ByteBuffer的类另有他人:
package cn.itcast.jvm.t1.direct;import sun.misc.Unsafe;import java.io.IOException;import java.lang.reflect.Field;/** * 直接内存分配的底层原理:Unsafe */public class Demo1_27 {static int _1Gb = 1024 * 1024 * 1024;public static void main(String[] args) throws IOException {// unsafe正常情况下不会使用,因为系统内存通常由系统自动控制 , 我们这里采用暴力反射获取Unsafe unsafe = getUnsafe();// 分配内存(base实际上是该内存的地址,所以我们在释放时同样提供该base地址)long base = unsafe.allocateMemory(_1Gb);unsafe.setMemory(base, _1Gb, (byte) 0);System.in.read();// 释放内存unsafe.freeMemory(base);System.in.read();}// 暴力反射获得unsafe对象public static Unsafe getUnsafe() {try {Field f = Unsafe.class.getDeclaredField("theUnsafe");f.setAccessible(true);Unsafe unsafe = (Unsafe) f.get(null);return unsafe;} catch (NoSuchFieldException | IllegalAccessException e) {throw new RuntimeException(e);}}}
然后我们就可以通过DirectMemory的源码来查看为什么它会收到jvm控制:// Primary constructor// DirectByteBuffer构造器里面直接调用了unsafe类来进行直接内存的控制DirectByteBuffer(int cap) {super(-1, 0, cap, cap);boolean pa = VM.isDirectMemoryPageAligned();int ps = Bits.pageSize();long size = Math.max(1L, (long)cap + (pa ? ps : 0));Bits.reserveMemory(size, cap);// 进行直接内存的生产long base = 0;try {base = unsafe.allocateMemory(size);} catch (OutOfMemoryError x) {Bits.unreserveMemory(size, cap);throw x;}unsafe.setMemory(base, size, (byte) 0);if (pa && (base % ps != 0)) {// Round up to page boundaryaddress = base + ps - (base & (ps - 1));} else {address = base;}// cleaner会自动检测directMemory是否还存在,若不存在调用该方法// 这里采用cleaner,直接创建一个新的类型的Deallocator,跳转到下面的类中cleaner = Cleaner.create(this, new Deallocator(base, size, cap));att = null;} // cleaner操作跳转的类,继承了Runnableprivate static class Deallocatorimplements Runnable{private static Unsafe unsafe = Unsafe.getUnsafe();private long address;private long size;private int capacity;private Deallocator(long address, long size, int capacity) {assert (address != 0);this.address = address;this.size = size;this.capacity = capacity;}// 被清理时调用下述方法,采用unsafe.freeMemory(address)来清理直接内存,所以我们的垃圾回收才能清理直接内存public void run() {if (address == 0) {// Paranoiareturn;}unsafe.freeMemory(address);address = 0;Bits.unreserveMemory(size, capacity);}}
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 【lwip】08-ARP协议一图笔记及源码实现
- 小米笔记本Pro15增强版评测_小米笔记本Pro15增强版评测表现
- 用一台笔记本电脑如何赚钱(笔记本电脑赚钱的办法)
- 四 【单片机入门】应用层软件开发的单片机学习之路-----ESP32开发板PWM控制电机以及中断的使用
- 笔记本电脑CF中烟雾头怎么调(win10cf新版本烟雾保护头怎么调)
- 笔记本电脑配置高低怎么区分(笔记本电脑看什么配置判断好坏)
- pytorch、paddlepaddle等环境搭建 深度学习环境搭建常用网址、conda/pip命令行整理
- 三十九 Java开发学习----SpringBoot整合mybatis
- Nacos基本学习
- 数据科学学习手札146 geopandas中拓扑非法问题的发现、诊断与修复