Java-DirectByteBufferMappedByteBuffer

Posted 冲上云霄的Jayden

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java-DirectByteBufferMappedByteBuffer相关的知识,希望对你有一定的参考价值。

内存管理

了解ByteBuffer之前,先看看计算机内存管理的几个术语:

  1. MMC:CPU的内存管理单元。
  2. 物理内存:即内存条的内存空间。
  3. 虚拟内存:计算机系统内存管理的一种技术。它使得应用程序认为它拥有连续的可用的内存(一个连续完整的地址空间),而实际上,它通常是被分隔成多个物理内存碎片,还有部分暂时存储在外部磁盘存储器上,在需要时进行数据交换。
  4. 页面文件:操作系统反映构建并使用虚拟内存的硬盘空间大小而创建的文件,在windows下,即pagefile.sys文件,其存在意味着物理内存被占满后,将暂时不用的数据移动到硬盘上。
  5. 缺页中断:当程序试图访问已映射在虚拟地址空间中但未被加载至物理内存的一个分页时,由MMC发出的中断。如果操作系统判断此次访问是有效的,则尝试将相关的页从虚拟内存文件中载入物理内存。

为什么会有虚拟内存和物理内存的区别?

如果正在运行的一个进程,它所需的内存是有可能大于内存条容量之和的,如内存条是256M,程序却要创建一个2G的数据区,那么所有数据不可能都加载到内存(物理内存),必然有数据要放到其他介质中(比如硬盘),待进程需要访问那部分数据时,再调度进入物理内存。

什么是虚拟内存地址和物理内存地址?

假设你的计算机是32位,那么它的地址总线是32位的,也就是它可以寻址00xFFFFFFFF(4G)的地址空间,但如果你的计算机只有256M的物理内存0x0x0FFFFFFF(256M),同时你的进程产生了一个不在这256M地址空间中的地址,那么计算机该如何处理呢?

回答这个问题前,先说明计算机的内存分页机制。

计算机会对虚拟内存地址空间(32位为4G)进行分页产生页(page),对物理内存地址空间(假设256M)进行分页产生页帧(page frame),页和页帧的大小一样,所以虚拟内存页的个数势必要大于物理内存页帧的个数。在计算机上有一个页表(page table),就是映射虚拟内存页到物理内存页的,更确切的说是页号到页帧号的映射,而且是一对一的映射。

虚拟内存页的个数 > 物理内存页帧的个数,岂不是有些虚拟内存页的地址永远没有对应的物理内存地址空间?

不是的,操作系统是这样处理的。操作系统有个页面失效(page fault)功能。操作系统找到一个最少使用的页帧,使之失效,并把它写入磁盘,随后把需要访问的页放到页帧中,并修改页表中的映射,保证了所有的页都会被调度。

DirectByteBuffer

DirectByteBuffer 直接缓冲区

DirectByteBuffer是一个特殊的ByteBuffer,底层同样需要一块连续的内存,操作模式与普通的ByteBuffer一致,但这块内存是调用unsafenative方法分配的堆外内存

直接缓冲区的内存释放也是由unsafenative方法完成的,DirectByteBuffer指向的内存通过PhantomReference持有,由JVM自行回收。

但如果DirectByteBuffer经过数次GC后进入老年代,就很可能由于Full GC间隔较长而长期存活,进而导致指向的堆外内存也无法回收。当需要手动回收时,需要通过反射调用DirectByteBuffer内部的Cleaner的clean私有方法。

为何要使用堆外内存?

Java应用一般能够操作的是JVM管理的堆内内存,一段数据从应用中发送至网络需要经过多次复制:

  1. 从堆内复制到堆外
  2. 从堆外复制到socket缓存
  3. socket缓存flush

DirectByteBuffer优缺点

使用直接缓冲区的优点:

  1. 这块缓冲区内存不受JVM直接管理回收
  2. 大小不受JVM分配的最大内存限制
  3. 一些IO操作可以避免堆外内存和堆内内存间的复制,比如网络传输
  4. 某些生命周期较长的大对象可以保存在堆外内存,减少对GC的影响

使用直接缓冲区的缺点:

  1. 不受JVM直接管理,容易造成堆外内存泄露
  2. 由于堆外内存并不能保存复杂对象而只能保存基本类型的包装类(底层都是byte array),因此要保存对象时需要序列化

MappedByteBuffer

MappedByteBuffer 内存映射缓冲区。

MappedByteBuffer与其他ByteBuffer一样底层是一段连续内存,区别在于这段内存使用的是内存映射的那段内存,也就是说对于这块缓冲区的数据修改会同步到对应的文件中。

FileChannel

NIO的Channel类型是一个通道,本身不能访问数据,而是与Buffer交互。

Channel类的作用主要是操作数据、数据传输、实现内存映射。

FileChannel分类

  1. FileChannel(文件)
  2. SocketChannel(客户端TCP)
  3. ServerSocketChannel(服务端TCP)
  4. DatagramChannel(UDP)

FileChannel优缺点

FileChannel优点:

  1. 内存映射的内容可以防止程序甭崩溃(kill -9)导致的数据丢失,这个特性在很多中间件系统中作用很大(阿里某些中间件比赛有要求kill -9不丢失)
  2. 不用阻塞等待,效率高
  3. 减少复制次数

FileChannel缺点:

  1. 由于内存映射需要指定映射文件大小,那么当映射的文件大小比写入的内容大时会产生文件间隙,即文件EOF后还有一部分无内容的填充,文件末尾乱码之类的,这个在实际应用中需要注意
  2. 映射后的内存页面需要等待被置换,导致系统的整体内存管理相对复杂

全网最细Docker安装Minio,填满最新版大坑(强烈推荐收藏)

文章目录


前言

通常在企业中我们会将一些图片,视频,文档等相关数据存储在对象存储中,常见的对象存储服务有阿里云的OSS对象存储、FastDFS分布式文件系统以及公司的私有云平台等等,以便于数据的存储和快速获取。但随着业务的快速发展,我们需要存储一些身份信息用于审核和实名相关的数据,这部分数据较为敏感,因此对于敏感数据的存储我们选择了使用兼容S3协议的开源分布式对象存储-Minio来进行自建服务。

一、简介

MinIO对象存储系统是为海量数据存储、人工智能、大数据分析而设计,基于Apache License v2.0开源协议的对象存储系统,它完全兼容Amazon S3接口,单个对象最大可达5TB,适合存储海量图片、视频、日志文件、备份数据和容器/虚拟机镜像等。MinIO主要采用Golang语言实现,整个系统都运行在操作系统的用户态空间,客户端与存储服务器之间采用http/https通信协议。

二、安装步骤

1.查询minio服务版本

docker search minio

2.拉取minio

执行命令 docker pull minio/minio 下载稳定版本镜像,使用命令 docker images 查看下载的镜像。

docker pull minio/minio

3.启动

如果是docker安装的,启动命令如下:

docker run  -p 9000:9000 --name minio \\
 -d --restart=always \\
 -e MINIO_ACCESS_KEY=minio \\
 -e MINIO_SECRET_KEY=minio@123 \\
 -v /usr/local/minio/data:/data \\
 -v /usr/local/minio/config:/root/.minio \\
  minio/minio server /data  --console-address ":9000" --address ":9090"

如果是linux版安装的,启动命令如下:

./minio server /usr/local/minio/data --console-address ":9090"

启动成功,IP+9000访问,我顿时傻逼了????


最新版长成这样了???心里奔溃啊,那个简洁的界面没了,我们需要安装以前的版本,所以您是在我写这篇文章之后才安装的minio,千万别装最新版的了,别用这个命令了:

docker pull minio/minio

4.docker hub下载其他版本

https://hub.docker.com/r/minio/minio/tags?page=1&ordering=last_updated

安装今年七月份以前的稳定版本。

docker pull minio/minio:RELEASE.2021-06-17T00-10-46Z-28-gac7697426

1、启动测试

docker run -p 9000:9000 minio/minio:RELEASE.2021-06-17T00-10-46Z-28-gac7697426 server /data


心态爆炸啊,这个版本也不行,我们继续找下一个版本。

三、下载安装旧版本

1、下载旧版本

通过以上的坑,我们发现minio的版本更新迭代特别快,最新版本的已经变得面目全非,不认识了。我查阅了最新官网发布版本。


大大小小已经发布了200多个版本了,我们继续找下一个版本,2021年6月17号的

docker pull minio/minio:RELEASE.2021-06-17T00-10-46Z

2、启动

docker run -p 9000:9000 minio/minio:RELEASE.2021-06-17T00-10-46Z server /data


看到这里我暗自大喜,终于填满了这个坑。

3、访问测试

输入IP+9000访问web页面:

还是熟悉的味道啊,真香!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

3、MinIO自定义Access和Secret密钥

docker run -p 9000:9000 minio/minio server /data

MinIO 需要一个持久卷来存储配置和应用数据。不过, 如果只是为了测试一下, 您可以通过简单地传递一个目录(在下面的示例中为/ data)启动MinIO。这个目录会在容器启动时在容器的文件系统中创建,不过所有的数据都会在容器退出时丢失。

所以我们在工作中,真正的开发中,不能这么干,以上命令仅供测试使用,那么在工作当中应该注意什么呢?首先你的key 和 secret是哼重要的,就相当于你的账号密码,不要设置的那么简单,还有就是你的文件存放,假设服务宕机了,文件也不丢失,服务迁移了,文件也能跟着迁移,这才是工作中应当注意的地方。

要覆盖MinIO的自动生成的密钥,您可以将Access和Secret密钥设为环境变量。 MinIO允许常规字符串作为Access和Secret密钥。

docker run -p 9000:9000 --name minio\\
  -e "MINIO_ACCESS_KEY=admin" \\
  -e "MINIO_SECRET_KEY=admin1973984292@qq.com" \\
  -v /usr/local/minio/data:/data \\
  -v /usr/local/minio/config:/root/.minio \\
  minio/minio:RELEASE.2021-06-17T00-10-46Z server /data
/usr/local/minio/data // 数据卷存放路径(自行添加)
/usr/local/minio/config //服务配置文件(自行添加)

再次启动之后,我们就可以发现重新登录,就可以使用我们自己的Access和Secret密钥了。


新建一个存储桶,往里面添加一个文件:


上传成功之后,我们去到我们的服务data数据卷存储位置,查看文件是否存在。

4、MinIO后台的方式启动

如果要后台运行 加入 -d 参数

docker run -d -p 9000:9000 --name minio\\
  -e "MINIO_ACCESS_KEY=admin" \\
  -e "MINIO_SECRET_KEY=admin1973984292@qq.com" \\
  -v /usr/local/minio/data:/data \\
  -v /usr/local/minio/config:/root/.minio \\
  minio/minio:RELEASE.2021-06-17T00-10-46Z server /data

另外小编给大家一个中文文档方便学习Minio:http://docs.minio.org.cn/docs/master/minio-docker-quickstart-guide

总结

好了,今天的文章就收尾了,在安装Minio的过程中,特别艰辛,最新版本实在巨坑,小编走了,不少弯路,喜欢的点赞,评论!!!!!

以上是关于Java-DirectByteBufferMappedByteBuffer的主要内容,如果未能解决你的问题,请参考以下文章