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.mzph.cn/news/599395.shtml

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

相关文章

FingerprintService启动-Android13

FingerprintService启动-Android13 1、指纹服务启动1.1 rc启动Binder对接指纹厂商TA库1.2 FingerprintService启动1.2.1 SystemServer启动FingerprintService1.2.2 注册Binder服务fingerprint 2、获取底层信息2.1 AIDL 对接TA中获取2.2 指纹类型判断 android13-release 1、指纹…

PyTorch基础操作

一、Tensor 在 PyTorch 中,张量(Tensor)是一个核心概念,它是一个用于存储和操作数据的多维数组,类似于 NumPy 的 ndarray,但与此同时,它也支持 GPU 加速,这使得在大规模数据上进行科…

CSS 放大翻转动画

<template><div class="container" @mouseenter="startAnimation" @mouseleave="stopAnimation"><!-- 旋方块 --><div class="box" :class="{ rotate-scale-up-hor: isAnimating }"><!-- 元素内…

F# 中更安全的递归

作者&#xff1a;David Schaefer 排版&#xff1a;Alan Wang 这是 David Schaefer 的客座博客文章。David 是一名专注于函数式编程的自由软件开发人员。他是 G-Research 开源团队的一员。他致力于改进 F# 开发者工具的生态系统。此外&#xff0c;他还帮助维护各种开源的 F# 项目…

使用爬虫爬取热门电影

文章目录 网站存储视频的原理M3U8文件解读网站分析代码实现 网站存储视频的原理 首先我们来了解一下网站存储视频的原理。 一般情况下&#xff0c;一个网页里想要显示出一个视频资源&#xff0c;必须有一个<video>标签&#xff0c; <video src"xxx.mp4"&…

【Python_PyQtGraph 学习笔记(九)】基于PlotWidget实现plot对象的坐标点添加标签

基于PlotWidget实现plot对象的坐标点添加标签 前言正文1、f_plotAddMark(self, xLst, yLst) 方法1、方法传参介绍2、方法内参数介绍2、方法调用3、案例完整代码4、实现效果前言 本文介绍如何在 PlotWidget 的 plot 对象坐标点上添加标签,也可以说是在 PlotWidget 上添加点的标…

Note: A Journey Across Canada

A Journey Across Canada 一场横穿加拿大的旅行 across journey After a quiz last autumn, Kuang crossed the continent eastward to Toronto to visit his schoolmate, the distance measuring approximately 5000 kilometers. 去年秋天一次考试后&#xff0c;Kuang向东穿…

数字人克隆系统开发公司?

广州硅基技术开发限公司是一家位于中国广东省广州市的科技公司。该公司专注于人工智能&#xff08;AI&#xff09;领域的研发和创新。广州硅基以技术创新和解决方案为核心&#xff0c;致力于为客户提供高质量的人工智能产品和服务。 广州硅基技术的主要业务包括但不限于&#…

Java中的序列化和反序列化:深入理解和实战

文章目录 1. 简介2. Java中的序列化3. Java中的反序列化4. Java序列化中的常见问题和解决策略5. 自定义序列化6. 常见问题及解答7. 使用场景8. 结尾 1. 简介 序列化和反序列化的本质是解决在进行远程通信和持久化数据时&#xff0c;如何保存和恢复数据的问题。 ** 序列化&…

stm32学习笔记:TIIM-输入捕获

输入捕获理论 4个输入捕获和输出比较通道&#xff0c;共用4个CCR寄存器 另外它们的CH1到CH4&#xff0c;4个通道的引脚&#xff0c;也是共用的。 所以对于同一个定时器&#xff0c;输入捕获和输出比较只能使用其中一个&#xff0c;不能同时使用。 电平跳变&#xff1a;上升沿…

《动手学深度学习》学习笔记 第5章 深度学习计算

本系列为《动手学深度学习》学习笔记 书籍链接&#xff1a;动手学深度学习 笔记是从第四章开始&#xff0c;前面三章为基础知道&#xff0c;有需要的可以自己去看看 关于本系列笔记&#xff1a; 书里为了让读者更好的理解&#xff0c;有大篇幅的描述性的文字&#xff0c;内容很…

Spring学习 Spring概述

1.1.Spring介绍 ​ Spring是轻量级Java EE应用开源框架&#xff08;官网&#xff1a; http://spring.io/ &#xff09;&#xff0c;它由Rod Johnson创为了解决企业级编程开发的复杂性而创建 1.2.简化应用开发体现在哪些方面&#xff1f; IOC 解决传统Web开发中硬编码所造成的…

python中collections.abc.Mapping 和collections.Mapping的区别

文章目录 在 Python 中&#xff0c;collections.abc.Mapping 和 collections.Mapping 都是用于表示映射类型&#xff08;即键值对的集合&#xff0c;例如字典&#xff09;的抽象基类。它们的区别在于它们的来源和使用方式。 collections.abc.Mapping 是 collections.abc 模块中…

1月5日代码随想录完全二叉树的节点个数

222.完全二叉树的节点个数 给你一棵 完全二叉树 的根节点 root &#xff0c;求出该树的节点个数。 完全二叉树 的定义如下&#xff1a;在完全二叉树中&#xff0c;除了最底层节点可能没填满外&#xff0c;其余每层节点数都达到最大值&#xff0c;并且最下面一层的节点都集中在…

使用ModelScope运行或者微调模型ModelScope国内一个“模型即服务”(MaaS)平台

TOC 一、ModelScope社区 ModelScope是一个“模型即服务”(MaaS)平台&#xff0c;由阿里推出维护&#xff0c;旨在汇集来自AI社区的最先进的机器学习模型&#xff0c;并简化在实际应用中使用AI模型的流程。ModelScope库使开发人员能够通过丰富的API设计执行推理、训练和评估&a…

即时设计:轻松实现设计稿动画,打造独具魅力的GIF作品

制作动画 随着动画设计越来越受欢迎&#xff0c;设计师们需要一款强大的工具&#xff0c;以便轻松控制设计稿元素的属性&#xff0c;实现动画效果。今天&#xff0c;我们向您推荐一款具备帧动画功能的设计工具&#xff0c;它可以让您轻松调整元素的宽高、相对位置等属性&#x…

Spring AI 指南

近年来&#xff0c;人工智能技术的迅猛发展改变了我们对科技的看法&#xff0c;并在各个领域引发了巨大的变革。每个人都希望在自己的项目上能够使用人工智能。Spring 框架提供了一个名为 “Spring AI” 的项目&#xff0c;Spring AI 项目旨在简化包含人工智能功能的应用程序的…

Python 基础面试第四弹

1. Python中常用的库有哪些&#xff0c;作用分别是什么 requests: requests 是一个用于发送 HTTP 请求的库&#xff0c;它提供了简单而优雅的 API&#xff0c;可以轻松地发送 GET、POST、PUT、DELETE 等请求&#xff0c;并处理响应数据。它支持会话管理、身份验证、文件上传等常…

Matlab绘制动态心形线

1. 代码 for alpha0:0.1:30 x-1.8:0.001:1.8; y(x.^2).^(1/3)0.9*(3.3-x.^2).^(1/2).*sin(alpha*pi*x); plot(x,y,r-,LineWidth,1.2); set(gca,YGrid,on); axis([-3,3,-2,4]); text(-2,3.35,$f(x)x^{\frac{2}{3}}0.9(3.3-x^2)^{\frac{1}{2}}sin(\alpha\pi x)$,Interpreter,lat…

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

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