JVM学习笔记——内存结构篇( 九 )

直接内存这小节我们来介绍系统中常用的直接内存
直接内存简介我们先来介绍一下直接内存的定义:

  • 直接内存不受JVM内存回收管理
  • 直接内存是直接受管于系统的内存,不能被JVM所调配
  • 直接内存通常用于NIO操作,用于数据缓冲区,其分配成本较高,但读写性能较高
直接内存详细介绍我们通过一段代码来展示直接内存的速度:
package cn.itcast.jvm.t1.direct;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;import java.nio.ByteBuffer;import java.nio.channels.FileChannel;/** * 演示 ByteBuffer 作用 */public class Demo1_9 {static final String FROM = "E:\\编程资料\\第三方教学视频\\youtube\\Getting Started with Spring Boot-sbPSjI4tt10.mp4";static final String TO = "E:\\a.mp4";static final int _1Mb = 1024 * 1024;public static void main(String[] args) {io(); // io 用时:1535.586957 1766.963399 1359.240226directBuffer(); // directBuffer 用时:479.295165 702.291454 562.56592}// directBuffer(直接内存读取数据)private static void directBuffer() {long start = System.nanoTime();try (FileChannel from = new FileInputStream(FROM).getChannel();FileChannel to = new FileOutputStream(TO).getChannel();) {ByteBuffer bb = ByteBuffer.allocateDirect(_1Mb);while (true) {int len = from.read(bb);if (len == -1) {break;}bb.flip();to.write(bb);bb.clear();}} catch (IOException e) {e.printStackTrace();}long end = System.nanoTime();System.out.println("directBuffer 用时:" + (end - start) / 1000_000.0);}// io(jvm正常读取数据)private static void io() {long start = System.nanoTime();try (FileInputStream from = new FileInputStream(FROM);FileOutputStream to = new FileOutputStream(TO);) {byte[] buf = new byte[_1Mb];while (true) {int len = from.read(buf);if (len == -1) {break;}to.write(buf, 0, len);}} catch (IOException e) {e.printStackTrace();}long end = System.nanoTime();System.out.println("io 用时:" + (end - start) / 1000_000.0);}}我们可以明显看到directBuffer速度比IO读取快很多,那么究竟是怎么实现的
我们可以分别给出两张图进行解释:
  1. JVM正常读取

JVM学习笔记——内存结构篇

文章插图
  1. 直接内存读取

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();}}

经验总结扩展阅读