首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >JVM内存与垃圾回收篇第11章直接内存

JVM内存与垃圾回收篇第11章直接内存

作者头像
yuanshuai
发布于 2022-08-17 06:35:26
发布于 2022-08-17 06:35:26
57400
代码可运行
举报
文章被收录于专栏:一只程序原一只程序原
运行总次数:0
代码可运行

第 11 章 直接内存

1、直接内存概述

直接内存

  1. 不是虚拟机运行时数据区的一部分,也不是《Java虚拟机规范》中定义的内存区域。
  2. 直接内存是在Java堆外的、直接向系统申请的内存区间。
  3. 来源于NIO,通过存在堆中的DirectByteBuffer操作Native内存
  4. 通常,访问直接内存的速度会优于Java堆。即读写性能高。
  5. 因此出于性能考虑,读写频繁的场合可能会考虑使用直接内存。
  6. Java的NIO库允许Java程序使用直接内存,用于数据缓冲区

代码示例

  • 代码
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 *  IO                  NIO (New IO / Non-Blocking IO)
 *  byte[] / char[]     Buffer
 *  Stream              Channel
 *
 * 查看直接内存的占用与释放
 * @author shkstart  shkstart@126.com
 * @create 2020  0:22
 */
public class BufferTest {
    private static final int BUFFER = 1024 * 1024 * 1024;//1GB

    public static void main(String[] args){
        //直接分配本地内存空间
        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(BUFFER);
        System.out.println("直接内存分配完毕,请求指示!");

        Scanner scanner = new Scanner(System.in);
        scanner.next();

        System.out.println("直接内存开始释放!");
        byteBuffer = null;
        System.gc();
        scanner.next();
    }
}
  • 呐,直接占用了 1G 的本地内存
  • 释放后,Java程序的内存占用明显减少

2、BIO 与 NIO

非直接缓存区(BIO)

原来采用BIO的架构,在读写本地文件时,我们需要从用户态切换成内核态

直接缓冲区(NIO)

NIO 直接操作物理磁盘,省去了中间商赚差价

代码测试

  • 测试代码:分别使用 BIO 和 NIO 复制大文件,看看用时差别
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * @author shkstart  shkstart@126.com
 * @create 2020  0:04
 */
public class BufferTest1 {

    private static final String TO = "F:\\test\\异界BD中字.mp4";
    private static final int _100Mb = 1024 * 1024 * 100;

    public static void main(String[] args) {
        long sum = 0;
        String src = "F:\\test\\异界BD中字.mp4";
        for (int i = 0; i < 3; i++) {
            String dest = "F:\\test\\异界BD中字_" + i + ".mp4";
            // sum += io(src,dest);//54606
            sum += directBuffer(src, dest);//50244
        }

        System.out.println("总花费的时间为:" + sum);
    }

    private static long directBuffer(String src, String dest) {
        long start = System.currentTimeMillis();

        FileChannel inChannel = null;
        FileChannel outChannel = null;
        try {
            inChannel = new FileInputStream(src).getChannel();
            outChannel = new FileOutputStream(dest).getChannel();

            ByteBuffer byteBuffer = ByteBuffer.allocateDirect(_100Mb);
            while (inChannel.read(byteBuffer) != -1) {
                byteBuffer.flip();//修改为读数据模式
                outChannel.write(byteBuffer);
                byteBuffer.clear();//清空
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (inChannel != null) {
                try {
                    inChannel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }
            if (outChannel != null) {
                try {
                    outChannel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }
        }

        long end = System.currentTimeMillis();
        return end - start;

    }

    private static long io(String src, String dest) {
        long start = System.currentTimeMillis();

        FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
            fis = new FileInputStream(src);
            fos = new FileOutputStream(dest);
            byte[] buffer = new byte[_100Mb];
            while (true) {
                int len = fis.read(buffer);
                if (len == -1) {
                    break;
                }
                fos.write(buffer, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }
        }


        long end = System.currentTimeMillis();

        return end - start;
    }
}

深入 ByteBuffer 源码

  • ByteBuffer.allocateDirect() 方法
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public static ByteBuffer allocateDirect(int capacity) {
    return new DirectByteBuffer(capacity);
}
  • DirectByteBuffer 类的构造器用到了 Unsafe 类分配本地内存
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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.create(this, new Deallocator(base, size, cap));
    att = null;



}

3、直接内存与 OOM

  1. 直接内存也可能导致OutofMemoryError异常
  2. 由于直接内存在Java堆外,因此它的大小不会直接受限于-Xmx指定的最大堆大小,但是系统内存是有限的,Java堆和直接内存的总和依然受限于操作系统能给出的最大内存。
  3. 直接内存的缺点为:
    • 分配回收成本较高
    • 不受JVM内存回收管理
  4. 直接内存大小可以通过MaxDirectMemorySize设置
  5. 如果不指定,默认与堆的最大值-Xmx参数值一致

代码示例 1

  • 代码
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * 本地内存的OOM:  OutOfMemoryError: Direct buffer memory
 *
 * @author shkstart  shkstart@126.com
 * @create 2020  0:09
 */
public class BufferTest2 {
    private static final int BUFFER = 1024 * 1024 * 20;//20MB

    public static void main(String[] args) {
        ArrayList<ByteBuffer> list = new ArrayList<>();

        int count = 0;
        try {
            while(true){
                ByteBuffer byteBuffer = ByteBuffer.allocateDirect(BUFFER);
                list.add(byteBuffer);
                count++;
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        } finally {
            System.out.println(count);
        }


    }
}
  • 本地内存持续增长,直至程序抛出异常:java.lang.OutOfMemoryError: Direct buffer memory
  • 异常信息
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
"C:\Program Files\Java\jdk1.8.0_144\bin\java" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2017.3.1\lib\idea_rt.jar=1296:C:\Program Files\JetBrains\IntelliJ IDEA 2017.3.1\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_144\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\rt.jar;C:\Users\Heygo\Desktop\JVMDemo\out\production\chapter11" com.atguigu.java.BufferTest2
180
Exception in thread "main" java.lang.OutOfMemoryError: Direct buffer memory
	at java.nio.Bits.reserveMemory(Bits.java:694)
	at java.nio.DirectByteBuffer.<init>(DirectByteBuffer.java:123)
	at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:311)
	at com.atguigu.java.BufferTest2.main(BufferTest2.java:21)

Process finished with exit code 1

代码示例 2

  • 代码:直接通过 Unsafe 类申请本地内存
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * -Xmx20m -XX:MaxDirectMemorySize=10m
 * @author shkstart  shkstart@126.com
 * @create 2020  0:36
 */
public class MaxDirectMemorySizeTest {
    private static final long _1MB = 1024 * 1024;

    public static void main(String[] args) throws IllegalAccessException {
        Field unsafeField = Unsafe.class.getDeclaredFields()[0];
        unsafeField.setAccessible(true);
        Unsafe unsafe = (Unsafe)unsafeField.get(null);
        while(true){
            unsafe.allocateMemory(_1MB);
        }

    }
}
  • JVM 参数
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
-Xmx20m -XX:MaxDirectMemorySize=10m
  • 抛出 OOM 异常
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
"C:\Program Files\Java\jdk1.8.0_144\bin\java" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2017.3.1\lib\idea_rt.jar=1362:C:\Program Files\JetBrains\IntelliJ IDEA 2017.3.1\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_144\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\rt.jar;C:\Users\Heygo\Desktop\JVMDemo\out\production\chapter11" com.atguigu.java.MaxDirectMemorySizeTestException in thread "main" java.lang.OutOfMemoryError	at sun.misc.Unsafe.allocateMemory(Native Method)	at com.atguigu.java.MaxDirectMemorySizeTest.main(MaxDirectMemorySizeTest.java:20)Process finished with exit code 1

JDK8 中元空间直接使用本地内存

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021.11.30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
Python中的端口协议之基于UDP协议
-------------------------------------------------------------------------------------------------------------------------------------
py3study
2020/01/16
1K0
第33天 初识socket编程
  MAC:  mac地址是在每一个计算机出厂的时候就会烧录进网卡内的一串数字,用来唯一的表示一台计算机
py3study
2020/01/19
4590
模拟ssh远程执行命令,粘包问题,基于socketserver实现并发的socket
输入tasklist命令,由于服务端发送字节多于1024字节,客户端只接受部分数据,并且当你再次输入dir命令的时候,客户端会接收dir命令的结果,但是会打印上一次的剩余未发送完的数据,这就是粘包问题
小小咸鱼YwY
2019/07/24
8360
模拟ssh远程执行命令,粘包问题,基于socketserver实现并发的socket
Python基础21-网络编程
-多年互联网运维工作经验,曾负责过大规模集群架构自动化运维管理工作。 -擅长Web集群架构与自动化运维,曾负责国内某大型金融公司运维工作。 -devops项目经理兼DBA。 -开发过一套自动化运维平台(功能如下): 1)整合了各个公有云API,自主创建云主机。 2)ELK自动化收集日志功能。 3)Saltstack自动化运维统一配置管理工具。 4)Git、Jenkins自动化代码上线及自动化测试平台。 5)堡垒机,连接Linux、Windows平台及日志审计。 6)SQL执行及审批流程。 7)慢查询日志分析web界面。
DriverZeng
2022/09/26
6250
Python基础21-网络编程
30.3. 企业级开发进阶2.3:UDP编程
回顾TCP协议:一个非常重要的数据传输协议,很多网络协议都是以TCP协议为基础的;TCP协议要求服务器和客户端通过三次握手交互的方式建立可靠的连接,然后再进行数据保温的发送,在发送过程中保证数据包的顺序和数量不会丢失,最后如果要断开连接需要四次挥手的方式进行连接的安全断开。
大牧莫邪
2018/08/27
4950
python网络编程-异常处理-异常捕获-抛出异常-断言-自定义异常-UDP通信-socketserver模块应用-03
异常:程序在运行过程中出现了不可预知的错误,并且该错误没有对应的处理机制,那么就会以异常的形式表现出来
suwanbin
2019/09/26
2.2K0
python网络编程-异常处理-异常捕获-抛出异常-断言-自定义异常-UDP通信-socketserver模块应用-03
NAT 原理以及 UDP 穿透
一直对 P2P 和 NAT 穿透的知识比较感兴趣,正巧最近看到一篇不需要第三方服务器实现 NAT 穿透的项目(https://github.com/samyk/pwnat),经过学习研究后发现这个项目也有很多局限性;借此机会,学习了下 NAT 原理和 UDP 穿透的实现。
Seebug漏洞平台
2021/04/23
4K0
NAT 原理以及 UDP 穿透
粘包问题的解决,上传与下载,多用户聊天
2.并可以返回终端的执行结果 subprocess.Popen(1,2,3,4) 1:cmd命令 2:shell = True 3:返回正确结果参数 stdout = subprocess.PIPE 4:返回错误的参数 stderr = subprocess.PIPE 返回的数据类型是二进制(bytes) 当res = stdout.read()+stderr.read()时 则正确的和错误的结果都可以返回
GH
2019/12/16
5260
粘包问题的解决,上传与下载,多用户聊天
asyncio模块
async可以定义协程,使用await可以针对耗时操作进行挂起,就与生成器的yield一样,函数交出控制权。协程遇到await,消息循环会挂起该协程,执行别的协程,直到其他协程也会挂起或者执行完毕,在进行下一次执行
星哥玩云
2022/09/08
6910
asyncio模块
diyupload插件:批量图片上传
/////分析:执行了两个操作: (1)、上传图片都服务器:fileUploadAction ////相应:
黄啊码
2020/05/31
4.4K0
Python协程
在所有的语言中都是层级调用的,比如A中调用B,B在执行过程中调用C,C执行完返回,B执行完返回,最后是A执行完毕。这是通过栈实现的,一个函数就是一个执行的子程序,子程序的调用总是有一个入口、一次返回,调用的顺序是明确的
星哥玩云
2022/09/08
3360
基于udp的套接字
1 ss = socket() #创建一个服务器的套接字 2 ss.bind() #绑定服务器套接字 3 inf_loop: #服务器无限循环 4 cs = ss.recvfrom()/ss.sendto() # 对话(接收与发送) 5 ss.close() # 关闭服务器套接字 udp客户端 cs = socket() # 创建客户套接字 comm_loop: # 通讯循环 cs.sendto()/c
超蛋lhy
2018/08/31
1.1K0
一文搞定MySQL盲注
无论是CTF还是实战渗透测试中,SQL注入都是一个非常热门的漏洞。通常人们根据SQL注入是否有回显将其分为有回显的注入和无回显的注入,其中无回显的注入顾名思义就是大家常说的盲注了。但是盲注不像union联合查询直接注出结果那么明了,利用起来也不是简单一两行SQL代码就可以完成,因此难度更大一些。 目前的CTF中MySQL的盲注依然是热点之一,然而盲注又被分成Like盲注、正则盲注、异或盲注等等太多类型,让新入门的萌新十分摸不到头脑。本文希望以言简意赅的语言帮助刚入门WEB CTF的选手们快速“拿捏”MySQL盲注。
Y1ng
2022/10/31
2.2K0
一文搞定MySQL盲注
Python 对文件的IO操作
程序结束自动关闭:程序结束时会释放文件对象的空间,文件会关闭,但是不建议这样来做,最好手动关闭
星哥玩云
2022/09/08
4120
10-angular 实例学习-1
controller 和 ng-options CSDN 链接 1.demo: <div ng-app="myApp" ng-init="number=1;cost=1" ng-controller="myCtr" class="wrapper"> <div class="money"> numebr: <input type="number" ng-model="number" name="" min="0" id="">
西南_张家辉
2021/02/02
5790
相关推荐
Python中的端口协议之基于UDP协议
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档