JVM学习-直接内存
直接内存
- 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学习-直接内存相关推荐
- JVM学习-Java内存模型JMM
目录 1.Java内存模型概述 2.原子性 2.1.问题提出 2.2.问题分析 2.3.解决办法 3.原子性 3.1.退不出的循环 3.2 解决方法 3.3 可见性 4.有序性 4.1.诡异的结果 4 ...
- JVM学习 - 体系结构 内存模型
2019独角兽企业重金招聘Python工程师标准>>> 一:Java技术体系模块图 二:JVM内存区域模型 1.方法区 也称"永久代" ."非堆&quo ...
- JVM学习--(二)内存模型、可见性、指令重排序
我们将根据JVM的内存模型探索java当中变量的可见性以及不同的java指令在并发时可能发生的指令重排序的情况. 内存模型 首先我们思考一下一个java线程要向另外一个线程进行通信,应该怎么做,我们再 ...
- JVM学习02——内存加载过程(类加载器)
类加载器 一.效果 class文件被load进内存,同时生成一个Class类的对象,可以用这个Class对象指向这块内容.(class类的对象不是new出来的,是hotspot中C++代码load出来 ...
- JVM学习-Java内存结构(详细易懂)
Java内存结构 1.JVM概述 2.程序计数器 2.1.定义 2.2.作用及特点解释 3.虚拟机栈 3.1.栈的特点 3.2.栈的演示 3.3.栈的问题辨析 3.4.栈的线程安全问题 3.5.栈内存 ...
- JVM学习 谁是垃圾?判断对象是否能被垃圾回收 可达性分析 四大引用
系统性学习JVM请点击JVM学习目录 为什么要垃圾回收 为什么要进行垃圾回收?做任何事之前我们都要搞清做这件事的原因.当我们在运行java项目时,如果项目比较大,它会实例化很多很多对象,多到我们的内存 ...
- JVM学习笔记(四)------内存调优
首先需要注意的是在对JVM内存调优的时候不能只看操作系统级别Java进程所占用的内存,这个数值不能准确的反应堆内存的真实占用情况,因为GC过后这个值是不会变化的,因此内存调优的时候要更多地使用JDK提 ...
- 【JVM学习笔记】内存回收与内存回收算法 就哪些地方需要回收、什么时候回收、如何回收三个问题进行分析和说明
目录 一.相关名词解释 垃圾收集常用名词 二.哪些地方需要回收 本地方法栈.虚拟机栈.程序计数器 方法区 Java堆 三.什么时候回收 1. 内存能否被回收 内存中的引用类型 引用计数算法 可达性分析 ...
- Java虚拟机(JVM)与Java内存模型(JMM)学习笔记
Java虚拟机[JVM]与Java内存模型[JMM]学习笔记 Java虚拟机(JVM) 三种JVM JVM 位置 JVM的主要组成部分及其作用 类加载器 双亲委派机制 沙箱安全机制 Java本地接口( ...
最新文章
- 关于VS2017使用中常见的几个问题
- 全国计算机二级考试c语言指针,全国计算机二级考试C语言 指针精讲课件.ppt
- VTK:绘制bottle瓶子用法实战
- 正确理解ContentPresenter
- java中hashmap_HashMap如何在Java中工作
- Javascript this关键字 指向详解
- windows等宽字体
- 支持XML的公司和它们的开发工具有哪些?
- img 显示base64_用 PySimpleGUI 做程序(7)--显示图片
- 谷歌浏览器代理服务器出现问题怎么办?(最快的解决办法)
- 深圳地铁五号线联网监控!
- 智能家居无创新,不智能
- 我看国内软件行业的发展方向
- 知网论文caj怎么转化成word
- Android开发高级进阶内涵段子APP项目实战视频教程
- 计算机基础考试题(答案在最后)
- 数据库的用户信息表设计
- python 申请内存_python内存分配
- 【硬件】标准阻值的由来
- Endnote生成GB/T7714-2005输出格式及中英文混排问题解决
热门文章
- 程序员与产品经理大打出手,公司的处理结果让所有人都笑开了花
- python字典和集合对象可以进行索引操作_python字典和列表的高级应用
- 我的docker随笔30:C++程序的自动化构建
- 我的内核学习笔记3:我的platform驱动模板文件
- python怎么做软件程序_Revit二次开发python怎么做?人工智能python语言在BIM软件高效建模的运用尝试...
- 【kafka】Kafka中的动态配置源码分析
- 【Flink】Flink 1.12.2 源码浅析 : TaskExecutor
- 【Elasticsearch】10分钟查询一个petabyte的云存储容量
- Elasticsearch】es 模糊查询导致Elasticsearch服务宕机
- 60-10-060-命令-kafka-run-class.sh