JVM篇:直接内存

news2024/11/16 11:50:35

直接内存

直接内存并不是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);
    }

    //NIO
    public 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();
        //通过暴力反射拿到底层类对象Unsafe
        try {
            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-private

        super(-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 boundary
            address = 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.coloradmin.cn/o/1359783.html

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

相关文章

产品设计的七个步骤详解

我们生活中使用的大大小小的东西都需要进行产品设计,那么什么是产品设计呢?产品设计是指从制定新产品设计任务到开发企业产品样品截止日期的一系列技术任务。让我分享一些产品设计步骤。 业务分析-要做什么业务分析 首先是制作业务流程图,这…

使用 dbgate 在 sealos 上完美管理 mysql pgsql 等数据库

先登录 sealos 创建数据库,可以创建个 pgsql: 再到模版市场启动 dbgate: 配置数据库的连接信息,即可搞定收工 sealos 以kubernetes为内核的云操作系统发行版,让云原生简单普及 laf 写代码像写博客一样简单,什么docker kubernete…

ubuntu18.04+realsenseD455制作TUM数据集

教程目录 一、本机环境二、安装RealSense SDK三、录制rosbag四、制作数据集四、安装ROS-RealSense五、测试数据集一、本机环境 Ubuntu系统ROS系统RealSense18.04melodicD455二、安装RealSense SDK 1、首先注册服务器的公钥 sudo apt-key adv --keyserver keyserver.ubuntu.co…

crontab定时任务不执行的原因

1.crond服务未启动 默认是开启的 2.权限问题 比如:脚本没有x执行权限, 解决方法: 增加执行权限,或者用bash abc.sh的方法执行 也有可能crontab任务所属的用户对某个目录没有写权限,也会失败 3.路径问题 建议使用绝…

【Angular 】Angular 模板中基于角色的访问控制

您是否在Angular模板中实现角色库访问控制?一种方法是通过*ngIf,但我不会选择该路径,因为它将在Angular模板中包含自定义函数,并且很难维护。正确的方法是使用Angular结构指令🚀. 什么是RBAC? 基于角色的…

【网络技术】【Kali Linux】Wireshark嗅探(七)超文本传送协议(HTTP)

一、实验目的 本次实验使用Wireshark流量分析工具进行网络嗅探,旨在了解超文本传送协议(HTTP)的工作原理。 二、HTTP协议概述 超文本传送协议( H yper T ext T ransfer P rotocol, HTTP)是互联网应用层的一个重要协…

Python 面向对象之继承和组合

Python 面向对象之继承和组合 【一】继承 【1】概念 继承是面向对象的三大特征之一继承允许一个类继承另一个类的属性和方法继承可以使代码重用,解决类与类之间代码重复的问题 【2】代码解释 不使用继承,创建豌豆射手类和豌豆的双发射手类 # 豌豆射…

C语言编译器(C语言编程软件)完全攻略(第六部分:VS2017下载地址和安装教程(图解))

介绍常用C语言编译器的安装、配置和使用。 六、VS2017下载地址和安装教程(图解) 继vs2015版本后,微软又推出了功能更加强大的VS 2017。 Visual Studio 2017 不仅支持 C#、C、Python、Visual Basic、Node.js、HTML、JavaScript等各大编程语言…

KK集团高管变更:陈世欣任总经理,涉无证放贷遭关注,还曾被处罚

近日,KK集团关联公司广东快客电子商务有限公司(下称“KK集团”)发生工商变更,其中郭惠波不再担任该公司总经理一职,由陈世欣接任。而在早前,陈世欣曾于2020年取代吴悦宁担任总经理职务,2021年7月…

用Python和Scrapy来构建强大的网络爬虫

前言 构建强大的网络爬虫是一个复杂而有挑战性的任务。Python和Scrapy是两个强大的工具,可以帮助我们完成这个任务。在本文中,我将向您展示如何使用Python和Scrapy构建一个强大的网络爬虫,并且还将介绍如何使用代理IP来更好地爬取目标网站。…

上帝视角俯视工厂设计模式

引言 本篇聊聊设计模式中的简单工厂、工厂方法、抽象工厂设计模式,争取在看完这篇后不会再傻傻分不清以及能够应用在实际项目中 背景 以一个咱们都熟悉的场景举个例子,我们平时都会戴口罩,用来过滤一些普通病毒,大致的设计如下…

操作系统期末复习知识点

目录 一.概论 1.操作系统的介绍 2.特性 3.主要功能 4.作用 二.进程的描述与控制 1.进程的定义 2.特性 3.进程的创建步骤 4.基本状态转化 5.PCB的作用 6.进程与线程的比较 三.进程同步 1.同步的概念(挺重要的) 2.临界区 3.管程和进程的区…

AI日报:大型律师事务所首次推出人工智能工具撰写合同

欢迎订阅专栏 《AI日报》 获取人工智能邻域最新资讯 总览 英国的Allen&Overy推出了一款可以为律师撰写合同的人工智能工具,名为ContractMatrix。 它利用现有的合同模板起草新的合同,律师可以接受或修改。 1000多名律师正在使用该工具。今年1月&…

LeetCode(39)组合总和⭐⭐

给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。 candidates 中的 同一个 数字可以 无限制重复被选取 。如…

史上最牛逼的fiddler抓包操作,【工具】Fiddler使用教程

eb调试工具之一, 它能记录所有客户端和服务器的http和https请求。允许你监视、设置断点、甚至修改输入输出数据。Fiddler包含了一个强大的基于事件脚本的子系统,并且能使用.net语言进行扩展。换言之,你对HTTP 协议越了解,你就能越…

033 - STM32学习笔记 - TIM定时器(一) - 高级定时器

033 - STM32学习笔记 - TIM定时器(一) - 高级定时器 上节内容学习了基本定时器,其功能比较简单,配置和使用也比较容易,今天在基本定时器的基础上学习一下高级控制定时器的内容。 在F429上一共有两个高级控制定时器和1…

给新手的25个建议

前言 最近知乎上,有一位大佬邀请我回答下面这个问题,看到这个问题我百感交集,感触颇多。 在我是新人时,如果有前辈能够指导方向一下,分享一些踩坑经历,或许会让我少走很多弯路,节省更多的学习的…

每日一道算法题day-three(备战蓝桥杯)

哈喽大家好,今天来给大家带来每日一道算法题系列第三天,让我们来看看今天的题目,一起备战蓝桥杯 题目: 小 Y的桌子上放着 n 个苹果从左到右排成一列,编号为从 11 到 n。 小苞是小 Y 的好朋友,每天她都会…

设计模式篇---命令模式(结合spring+动态代理实现开闭)

文章目录 概念结构实例总结 概念 命令模式:将一个请求封装为一个对象,从而可用不同的请求对客户进行参数化,对请求排队或者记录请求日志,以及支持可撤销的操作。 现实生活中,我们用开关来控制一些电器的打开和关闭&am…

NLP电影情绪分析项目

https://machinelearningmastery.com/develop-word-embedding-model-predicting-movie-review-sentiment/ https://machinelearningmastery.com/prepare-movie-review-data-sentiment-analysis/ 本教程分为 5 个部分;他们是: 电影评论数据集数据准备训练嵌入层训练…