JVM篇:直接内存

直接内存

直接内存并不是JVM的内存结构,直接内存是操作系统的内存,Java本身并不能对操作系统的内存进行操作,而是通过调用本地方法。直接内存常用于NIO作为缓冲区存在,分配成本较高但是读写性能好,并且不受JVM内存回收管理

NIO与IO的区别

public class demo5 {private static final String From = "下载文件路径";private static final String TO = "保存文件路径";private static final int _1MB = 1024 * 1024;public static void main(String[] args) {io();directBuffer();}public static void io() {long start = System.nanoTime();//开始时间byte[] buf = new byte[_1MB];try {FileInputStream inputStream = new FileInputStream(From);FileOutputStream outputStream = new FileOutputStream(TO);while (true) {int len = inputStream.read(buf);if (len == -1) {break;}outputStream.write(buf);}} catch (Exception e) {e.printStackTrace();}long end = System.nanoTime();System.out.println(end - start);}//NIOpublic static void directBuffer() {long start = System.nanoTime();//开始时间try (FileChannel channel = new FileInputStream(From).getChannel();FileChannel to = new FileOutputStream(TO).getChannel()) {ByteBuffer buf = ByteBuffer.allocateDirect(_1MB);while (true) {int len = channel.read(buf);if (len == -1) {break;}//flip()大概意思是记录当前的缓冲位置,下次读入缓冲区从保存的位置开始读取buf.flip();to.write(buf);buf.clear();}} catch (Exception e) {e.printStackTrace();}long end = System.nanoTime();System.out.println(end - start);}
}

对同一个文件进行下载保存操作,使用IO要比NIO慢很多,这个时候就要看IO与NIO的实现原理了。

IO实现原理

Java在运行到读取文件时,由于Java本身不能对操作系统的内存进行读取,所以需要调用本地方法对操作系统内存进行操作(也就是上图CPU时间轴的System部分),操作系统需要从磁盘文件读取文件到系统的缓冲空间(保存的第一份),系统缓冲区再写入Java的缓冲区(程序中定义的byte数组充当缓冲区,相当于二次保存),然后本地方法调用结束,CPU再转换到Java程序去读取Java缓冲区保存。

NIO实现原理

NIO与IO的区别在于操作系统会分出一块直接内存,这块内存java可以直接访问到,省去了操作系统的缓冲区到Java缓冲区的部分。因此读写性能比较好。对应的代码为ByteBuffer.allocateDirect()

直接内存的回收原理

这是还未进行分配直接内存是内存占用比为47%。

public class demo6 {public static void main(String[] args) throws IOException {//使用直接内存并分配1G大小ByteBuffer byteBuffer = ByteBuffer.allocateDirect(1024 * 1024 * 1024);System.out.println("分配完成");System.in.read();//将引用置空,使其可以被回收byteBuffer = null;System.gc();System.out.println("释放完成");System.in.read();}
}

接下来观察内存占用比

加了1G的直接内存后,占比为54%接下来调用gc垃圾回收

可以看到内存恢复为47%,说明直接内存被释放,但是直接内存是不受GC回收管理的,为什么会被释放呢?

实际上释放直接内存是JVM自己完成的,由Java底层Unsafe类实现。简单模拟一下

public class demo7 {public static void main(String[] args) throws IOException {Unsafe unsafe = getUnsafe();//base是指分配的内存地址long base = unsafe.allocateMemory(1024 * 1024 * 1024);unsafe.setMemory(base,1024 * 1024 * 1024,(byte) 0);System.in.read();unsafe.freeMemory(base);System.in.read();}public static Unsafe getUnsafe() {
//        Unsafe unsafe = Unsafe.getUnsafe();//通过暴力反射拿到底层类对象Unsafetry {Field f = Unsafe.class.getDeclaredField("theUnsafe");f.setAccessible(true);Unsafe unsafe = (Unsafe) f.get(null);return unsafe;} catch (NoSuchFieldException | IllegalAccessException e) {e.printStackTrace();throw new RuntimeException(e);}}
}

上面代码手动释放直接内存的片段执行结果和第一个例子结果完全相同。那么我们去看一下ByteBuffer.allocateDirect()方法源码

该方法创建了一个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 = Cleaner.create(this, new Deallocator(base, size, cap));att = null;}

Clearner类型在Java的类库中叫做虚引用类型,特点是虚引用所关联的对象被GC回收时,会自动触发create方法。在JVM中有一个单独线程监视虚引用对象的状态,如果关联对象被回收就会执行对应的run方法。

由这个构造方法可以看出来,从第9行开始,做了手动释放直接内存代码块相同的事情。都是调用Unsafe对象去分配内存空间,不同的是,它创建了一个Cleaner对象,并调用了create方法,查看这个方法参数Deallocator对象源码

可以看出来它实现了Runnable接口,相当于由其他线程去执行run方法而不是主线程。run方法中调用了Unsafe的freeMemory()方法释放内存。

总结就是:在客户端分配直接内存时,创建了一个Clearner对象与客户端对象相绑定,当客户端对象被垃圾回收时,就会执行虚引用监视线程中的任务线程由JVM释放直接内存。

禁用显式回收对直接内存的影响

所谓禁用显式回收就是在运行前添加的一个JVM参数-XX:+DisableExplicitGC,添加该参数后,在代码中程序员编写的System.gc()就无法生效(因为手动的gc操作是一个Full GC是一个耗时比较久的操作,因此在大多时候,等待程序自己进行gc即可,手动的GC会影响程序运行效率。)由于手动gc失效,那么在JVM内存充足的情况下,与之关联的对象即使为null也不会立即被回收,那么直接内存也无法释放。为了避免这个问题,我们可以在频繁操作直接内存时,通过调用Unsafe类中的freeMemory方法来手动释放直接内存。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/231593.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

有网友希望我推荐几个创建产品手册工具,这不就来了!

上次我有说到,企业应该充分认识到产品手册的重要性,并采取有效的策略和措施来制作和传播高质量的产品手册,以提升品牌知名度和市场份额。后台有网友问我除了设计排版的那种产品手册工具,还有什么方式可以去做产品手册。今天就介绍…

Verifiable Credentials可验证证书 2023 终极指南

1. 引言 Dock公司为去中心化数字身份领域的先驱者,其自2017年以来,已知专注于构建前沿的可验证证书(Verifiable Credentials)技术。本文将阐述何为电子证书、电子证书工作原理、以及其对组合和个人的重要性。 伪造实物证书和数字…

​结构体数组

1. 结构体的声明 1.1 结构体的基础知识 结构是一些值的集合,这些值被称为成员变量。结构的每个成员可以是不同类型的变量。 1.2 结构的声明 struct tag {member - list; }variable-list; 例:描述一个人的信息:名字电话性别身高 //声明的…

05、Kafka ------ CMAK 各个功能的作用解释(主题和分区 详解,用命令行和图形界面创建主题和查看主题)

目录 CMAK 各个功能的作用解释(主题)★ 主题★ 分区★ 创建主题:★ 列出和查看主题 CMAK 各个功能的作用解释(主题) ★ 主题 Kafka 主题虽然也叫 topic,但它和 Pub-Sub 消息模型中 topic 主题及 AMQP 的 t…

基于多反应堆的高并发服务器【C/C++/Reactor】(中)创建一个TcpConnection实例 以及 接收客户端数据

#CSDN 年度征文|回顾 2023,赢专属铭牌等定制奖品# 一、主线程反应堆模型的事件添加和处理详解 >>服务器和客户端建立连接和通信流程: 基于多反应堆模型的服务器结构图,这主要是一个TcpServer,关于HttpServer,…

4个原创技术文档,从Excel到MySQL到Python

2023马上就要结束了,回首这一年的工作和努力,我感到非常欣慰和自豪。在这段时间里,我专注于撰写原创技术文档,致力于为大家提供有价值的内容。 这四篇原创技术文档是我精心编写的,每一篇都经过了深入研究和详尽的实践…

HTML5大作业-精致版个人博客空间模板源码

文章目录 1.设计来源1.1 博客主页界面1.2 博主信息界面1.3 我的文章界面1.4 我的相册界面1.5 我的工具界面1.6 我的源码界面1.7 我的日记界面1.8 我的留言板界面1.9 联系博主界面 2.演示效果和结构及源码2.1 效果演示2.2 目录结构2.3 源代码 源码下载 作者:xcLeigh …

普中STM32-PZ6806L开发板(HAL库函数实现-按键扫描)

简介 实现按键扫描, 实现四个按键按下控制灯的亮灭 电路原理图 按键电路原理图 按键与主芯片引脚原理图 其他知识 原理图分析 Key_UP按下会有高电平输入, 所以电路设置应该是默认低电平, 初始化为下拉输入 Key_Left/Right/Down按下会有低电平, 初始化为下拉输…

相机同步遇到的小问题

出现问题 在进行两个相机显示的时候,出现了相机显示不同步的情况,具体情况如下视频所示: 华睿/大华相机左右相机显示不同步 可以见到视频之中,右相机是比左相机更快一点的,但是有的时候就是同步的。我调用的代码是现成…

最新GPT4.0使用教程,AI绘画,ChatFile文档对话总结+GPT语音对话使用,DALL-E3文生图

一、前言 ChatGPT3.5、GPT4.0、GPT语音对话、Midjourney绘画,文档对话总结DALL-E3文生图,相信对大家应该不感到陌生吧?简单来说,GPT-4技术比之前的GPT-3.5相对来说更加智能,会根据用户的要求生成多种内容甚至也可以和…

亚信安慧AntDB数据库:企业核心业务系统数据库升级改造的可靠之选

在近期召开的“2023年国有企业应用场景发布会”上,亚信安慧公司的核心数据库产品AntDB闪耀登场,技术总监北陌先生针对企业核心业务系统数据库升级改造的关键议题发表了深度分享。他从研发、工程实施和运维管理三个维度细致剖析了当前企业在进行数据库升级…

Linux系统安装MySQL

Linux系统安装MySQL 第一步:下载YUM wget http://dev.mysql.com/get/mysql57-community-release-el7-11.noarch.rpm第二步:安装MySQL的YUM 仓库 rpm -ivh mysql57-community-release-el7-11.noarch.rpm第三步:查看MySQL版本 yum repolist …

嵌入式科普(9)vscode无法跳转和恢复默认配置

一、目的/概述 二、解决办法 2.1 使能Intelli Sense Engine 2.2 vscode恢复默认配置 2.3 c/c与clangd冲突 嵌入式科普(9)vscode无法跳转和恢复默认配置 一、目的/概述 1、2024年的第一天突然vscode无法跳转,莫名其妙 2、尝试了各种设置和插件都无效,卸…

【萤火虫系列教程】1/5-Adobe Firefly 注册账号

001-Adobe Firefly 注册账号 AI时代如火如荼,Adobe也不甘落后,于今年3月份发布AI创意生成工具Firefly(中文翻译:萤火虫) Adobe Firefly简介 Adobe Firefly的官方介绍为:Firefly是Adobe产品中新的创意生成…

YOLOv8模型yaml结构图理解(逐层分析)

前言 YOLO-V8(官网地址):https://github.com/ultralytics/ultralytics 一、yolov8配置yaml文件 YOLOv8的配置文件定义了模型的关键参数和结构,包括类别数、模型尺寸、骨架(backbone)和头部(hea…

Geotrust DV通配符证书保护域名数量

Geotrust是一家知名的SSL证书提供商,旗下有多种类型的SSL数字证书,保护网站数据在传输过程中的安全性和完整性,帮助用户确认其网站的安全。通配符SSL证书是Geotrust颁发的一种可以同时保护多个域名站点的SSL证书。今天就随SSL盾小编了解Geotr…

Jmeter 性能 —— 电商系统TPS计算

1、怎么计算得出TPS指标 ①第一个通过运维那边给的生产数据,看一下生产进件有多少,计算得来的,如果没有生产数据,或者不过就看如下的方法 ②第二个就是根据最近一个月的实际访问数据,比如每天调用了多少个接口&#…

将 validator 校验器从 ParameterValidator 中抽离出来

目录 一、前置说明1、总体目录2、相关回顾3、本节目标 二、操作步骤1、项目目录2、代码实现3、测试代码4、日志输出 三、后置说明1、要点小结2、下节准备 一、前置说明 1、总体目录 《 pyparamvalidate 参数校验器,从编码到发布全过程》 2、相关回顾 pyparamval…

01、Kafka ------ 下载、安装 ZooKeeper 和 Kafka

目录 Kafka是什么?安装 ZooKeeper下载安装启动 zookeeper 服务器端启动 zookeeper 的命令行客户端工具 安装 Kafka下载安装启动 Kafka 服务器 Kafka是什么? RabbitMQ的性能比ActiveMQ的性能有显著提升。 Kafka的性能比RabbitMQ的性能又有显著提升。 K…

【React系列】react-router

本文来自#React系列教程:https://mp.weixin.qq.com/mp/appmsgalbum?__bizMzg5MDAzNzkwNA&actiongetalbum&album_id1566025152667107329) 一. 认识react-router 1.2. 前端路由原理 前端路由是如何做到URL和内容进行映射呢?监听URL的改变。 UR…