直接内存

  • 1.直接内存概述
  • 2.基本使用
  • 3.内存溢出
  • 4.释放原理

1.直接内存概述

它不能存在于JVM内存结构中

属于操作系统,常见于NIO操作时,用于数据缓冲区
bytebuffer所使用的内存就是直接内存
分配回收成本较高,但读写性能高

不受JVM内存回收管理

通过以下程序测试直接内存的读写性能

/*** 演示 ByteBuffer 作用*/
public class Demo1_9 {//文件大概200Mstatic 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;        //    //读写缓冲区代码都使用1Mpublic static void main(String[] args) {io(); // io 用时:1535.586957 1766.963399 1359.240226directBuffer(); // directBuffer 用时:479.295165 702.291454 562.56592}
//使用byprivate 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);}//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);}
}

2.基本使用


为了使用直接内存,文件读写效率会大大提高。
Java要想读写磁盘,就需要调用操作系统提供的函数,也就是之前native函数。CPU状态会从用户态切换到内核态。当切换到内核态之后,CPU函数就能读取磁盘文件的内容了。内核态的时候,会在操作系统内核中划出一块缓冲区,称之为系统缓存区。分次进行读取。此时java代码不能够访问,Java又会在堆内存中划分一块java缓冲区,**然后再从系统缓存区中读取到Java缓冲区,**然后调用输入输出流的读写操作进行读写。
此时有两块缓冲区。这样就造成了不必要的数据复制。

direct memory:当我们调用bytebuffer的allocateDirect方法时,就会分配一块直接内存,我们会在操作系统这边划出一块缓冲区,与之前不一样的是,操作系统划出的这块内存,java代码可以直接访问。对两端内存都是共享的。比之前少了一次缓冲区的复制操作,文件读取效率得到了大的提升。
直接内存是操作系统和Java代码都可以访问的一块区域,无需将代码从系统内存复制到Java堆内存,从而提高了效率

3.内存溢出

direct memory不受Java虚拟机回收管理,不会去释放directmemory所占用的内存。

public class Demo1_10 {static int _100Mb = 1024 * 1024 * 100;   //每次分配100M的内存public static void main(String[] args) {List<ByteBuffer> list = new ArrayList<>();int i = 0;try {while (true) {//循环多次ByteBuffer byteBuffer = ByteBuffer.allocateDirect(_100Mb);list.add(byteBuffer);i++;}} finally {System.out.println(i);}// 方法区是jvm规范, jdk6 中对方法区的实现称为永久代//                  jdk8 对方法区的实现称为元空间}
}


循环了17次,报了Java.lang.OutOfMemoryError:Direct buffer memory错误

4.释放原理

直接内存的回收不是通过JVM的垃圾回收来释放的,而是通过unsafe.freeMemory来手动释放

通过

//通过ByteBuffer申请1M的直接内存
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(_1M);

申请直接内存,但JVM并不能回收直接内存中的内容,它是如何实现回收的呢?

allocateDirect的实现

public static ByteBuffer allocateDirect(int capacity) {return new DirectByteBuffer(capacity);
}

DirectByteBuffer类

DirectByteBuffer(int cap) {   // package-privatesuper(-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 = Cleaner.create(this, new Deallocator(base, size, cap)); //通过虚引用,来实现直接内存的释放,this为虚引用的实际对象att = null;
}

这里调用了一个Cleaner的create方法,且后台线程还会对虚引用的对象监测,如果虚引用的实际对象(这里是DirectByteBuffer)被回收以后,就会调用Cleaner的clean方法,来清除直接内存中占用的内存

public void clean() {if (remove(this)) {try {this.thunk.run(); //调用run方法} catch (final Throwable var2) {AccessController.doPrivileged(new PrivilegedAction<Void>() {public Void run() {if (System.err != null) {(new Error("Cleaner terminated abnormally", var2)).printStackTrace();}System.exit(1);return null;}});}

对应对象的run方法

public void run() {if (address == 0) {// Paranoiareturn;}unsafe.freeMemory(address); //释放直接内存中占用的内存address = 0;Bits.unreserveMemory(size, capacity);
}

直接内存的回收机制总结

使用了Unsafe类来完成直接内存的分配回收,回收需要主动调用freeMemory方法

ByteBuffer的实现内部使用了Cleaner(虚引用)来检测ByteBuffer。一旦ByteBuffer被垃圾回收,那么会由ReferenceHandler来调用Cleaner的clean方法调用freeMemory来释放内存

JVM学习-直接内存相关推荐

  1. JVM学习-Java内存模型JMM

    目录 1.Java内存模型概述 2.原子性 2.1.问题提出 2.2.问题分析 2.3.解决办法 3.原子性 3.1.退不出的循环 3.2 解决方法 3.3 可见性 4.有序性 4.1.诡异的结果 4 ...

  2. JVM学习 - 体系结构 内存模型

    2019独角兽企业重金招聘Python工程师标准>>> 一:Java技术体系模块图 二:JVM内存区域模型 1.方法区 也称"永久代" ."非堆&quo ...

  3. JVM学习--(二)内存模型、可见性、指令重排序

    我们将根据JVM的内存模型探索java当中变量的可见性以及不同的java指令在并发时可能发生的指令重排序的情况. 内存模型 首先我们思考一下一个java线程要向另外一个线程进行通信,应该怎么做,我们再 ...

  4. JVM学习02——内存加载过程(类加载器)

    类加载器 一.效果 class文件被load进内存,同时生成一个Class类的对象,可以用这个Class对象指向这块内容.(class类的对象不是new出来的,是hotspot中C++代码load出来 ...

  5. JVM学习-Java内存结构(详细易懂)

    Java内存结构 1.JVM概述 2.程序计数器 2.1.定义 2.2.作用及特点解释 3.虚拟机栈 3.1.栈的特点 3.2.栈的演示 3.3.栈的问题辨析 3.4.栈的线程安全问题 3.5.栈内存 ...

  6. JVM学习 谁是垃圾?判断对象是否能被垃圾回收 可达性分析 四大引用

    系统性学习JVM请点击JVM学习目录 为什么要垃圾回收 为什么要进行垃圾回收?做任何事之前我们都要搞清做这件事的原因.当我们在运行java项目时,如果项目比较大,它会实例化很多很多对象,多到我们的内存 ...

  7. JVM学习笔记(四)------内存调优

    首先需要注意的是在对JVM内存调优的时候不能只看操作系统级别Java进程所占用的内存,这个数值不能准确的反应堆内存的真实占用情况,因为GC过后这个值是不会变化的,因此内存调优的时候要更多地使用JDK提 ...

  8. 【JVM学习笔记】内存回收与内存回收算法 就哪些地方需要回收、什么时候回收、如何回收三个问题进行分析和说明

    目录 一.相关名词解释 垃圾收集常用名词 二.哪些地方需要回收 本地方法栈.虚拟机栈.程序计数器 方法区 Java堆 三.什么时候回收 1. 内存能否被回收 内存中的引用类型 引用计数算法 可达性分析 ...

  9. Java虚拟机(JVM)与Java内存模型(JMM)学习笔记

    Java虚拟机[JVM]与Java内存模型[JMM]学习笔记 Java虚拟机(JVM) 三种JVM JVM 位置 JVM的主要组成部分及其作用 类加载器 双亲委派机制 沙箱安全机制 Java本地接口( ...

最新文章

  1. 关于VS2017使用中常见的几个问题
  2. 全国计算机二级考试c语言指针,全国计算机二级考试C语言 指针精讲课件.ppt
  3. VTK:绘制bottle瓶子用法实战
  4. 正确理解ContentPresenter
  5. java中hashmap_HashMap如何在Java中工作
  6. Javascript this关键字 指向详解
  7. windows等宽字体
  8. 支持XML的公司和它们的开发工具有哪些?
  9. img 显示base64_用 PySimpleGUI 做程序(7)--显示图片
  10. 谷歌浏览器代理服务器出现问题怎么办?(最快的解决办法)
  11. 深圳地铁五号线联网监控!
  12. 智能家居无创新,不智能
  13. 我看国内软件行业的发展方向
  14. 知网论文caj怎么转化成word
  15. Android开发高级进阶内涵段子APP项目实战视频教程
  16. 计算机基础考试题(答案在最后)
  17. 数据库的用户信息表设计
  18. python 申请内存_python内存分配
  19. 【硬件】标准阻值的由来
  20. Endnote生成GB/T7714-2005输出格式及中英文混排问题解决

热门文章

  1. 程序员与产品经理大打出手,公司的处理结果让所有人都笑开了花
  2. python字典和集合对象可以进行索引操作_python字典和列表的高级应用
  3. 我的docker随笔30:C++程序的自动化构建
  4. 我的内核学习笔记3:我的platform驱动模板文件
  5. python怎么做软件程序_Revit二次开发python怎么做?人工智能python语言在BIM软件高效建模的运用尝试...
  6. 【kafka】Kafka中的动态配置源码分析
  7. 【Flink】Flink 1.12.2 源码浅析 : TaskExecutor
  8. 【Elasticsearch】10分钟查询一个petabyte的云存储容量
  9. Elasticsearch】es 模糊查询导致Elasticsearch服务宕机
  10. 60-10-060-命令-kafka-run-class.sh