堆外内存操作类ByteBuffer
本篇主要讲解如何使用直接内存(堆外内存),并按照下面的步骤进行说明:
相关背景-->读写操作-->关键属性-->读写实践-->扩展-->参考说明
希望对想使用直接内存的朋友,提供点快捷的参考。
数据类型
下面这些,都是在使用DirectBuffer
中必备的一些常识,暂作了解吧!
基本类型长度
在Java中有很多的基本类型,比如:
byte
,一个字节是8位bit,也就是1Bshort
,16位bit,也就是2Bint
,32位bit,也就是4Blong
, 64位bit,也就是8Bchar
,16位bit,也就是2Bfloat
,32位bit,也就是4Bdouble
,64位bit,也就是8B
不同的类型都会按照自己的位数来存储,并且可以自动进行转换提升。byte
、char
、short
都可以自动提升为int
,如果操作数有long
,就会自动提升为long
,float
和double
也是如此。
大端小端
由于一个数据类型可能有很多个字节组成的,那么它们是如何摆放的。这个是有讲究的:
- 大端:低地址位 存放 高有效字节
- 小端:低地址位 存放 低有效字节
举个例子,一个char
是有两个字节组成的,这两个字节存储可能会显示成如下的模样,比如字符a
:
1
2
3
|
低地址位 高地址位 大端; 00 96 小端: 96 00 |
String与new String的区别
再说说"hello"
和new String("hello")
的区别:
如果是"hello"
,JVM会先去共享的字符串池中查找,有没有"hello"
这个词,如果有直接返回它的引用;如果没有,就会创建这个对象,再返回。因此,"a"+"b"
相当于存在3个对象,分别是"a"
、"b"
、"ab"
。
而new String("hello")
,则省去了查找的过程,直接就创建一个hello
的对象,并且返回引用。
读写数据
在直接内存中,通过allocateDirect(int byte_length)
申请直接内存。这段内存可以理解为一段普通的基于Byte
的数组,因此插入和读取都跟普通的数组差不多。ByteBuffer中的allocateDirect方法调用DirectByteBuffer。
public static ByteBuffer allocateDirect(int capacity) {
return new DirectByteBuffer(capacity);
}
只不过提供了基于不同数据类型的插入方法,比如:
- put(byte) 插入一个byte
- put(byte[]) 插入一个byte数组
- putChar(char) 插入字符
- putInt(int) 插入Int
- putLong(long) 插入long
等等….详细的使用方法,也可以参考下面的图片:
对应读取数据,跟写入差不多:
注意所有没有index参数的方法,都是按照当前position的位置进行操作的。
下面看看什么是position,还有什么其他的属性吧!
基本的属性值
它有几个关键的指标:
1
|
mark-->position-->limit-->capacity |
另外,还有remaining=limit-position
。
先说说他们的意思吧!
当前位置——position
position是当前数组的指针,指示当前数据位置。举个例子:
ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
buffer.putChar('a');
System.out.println(buffer);
buffer.putChar('c');
System.out.println(buffer);
buffer.putInt(10);
System.out.println(buffer);
由于一个char是2个字节,一个Int是4个字节,因此position的位置分别是:
2,4,8
注意,Position的位置是插入数据的当前位置,如果插入数据,就会自动后移。
也就是说,如果存储的是两个字节的数据,position的位置是在第三个字节上,下标就是2。
java.nio.DirectByteBuffer[pos=2 lim=1024 cap=1024]
java.nio.DirectByteBuffer[pos=4 lim=1024 cap=1024]
java.nio.DirectByteBuffer[pos=8 lim=1024 cap=1024]
- position可以通过position()获得,也可以通过position(int)设置。
//position(int)方法的源码
public final Buffer position(int newPosition) {
if ((newPosition > limit) || (newPosition < 0))
throw new IllegalArgumentException();
position = newPosition;
if (mark > position) mark = -1;
return this;
}
注意:position的位置要比limit小,比mark大
空间容量——capacity
capacity
是当前申请的直接内存的容量,它是申请后就不会改变的。
- capacity则可以通过capacity()方法获得。
限制大小——limit
我们可能想要改变这段直接内存的大小,因此可以通过一个叫做Limit的属性设置。
- limit则可以通过limit()获得,通过limit(int)进行设置。
注意limit要比mark和position大,比capacity小。
//limit(int)方法的源码
public final Buffer limit(int newLimit) {
if ((newLimit > capacity) || (newLimit < 0))
throw new IllegalArgumentException();
limit = newLimit;
if (position > limit) position = limit;
if (mark > limit) mark = -1;
return this;
}
标记位置——mark
mark,就是一个标记为而已,记录当前的position的值。常用的场景,就是记录某一次插入数据的位置,方便下一次进行回溯。
- 可以使用
mark()
方法进行标记, - 使用
reset()
方法进行清除, - 使用
rewind()
方法进行初始化//mark方法标记当前的position,默认为-1
public final Buffer mark() {
mark = position;
return this;
}
//reset方法重置mark的位置,position的位置,不能小于mark的位置,否则会出错
public final Buffer reset() {
int m = mark;
if (m < 0)
throw new InvalidMarkException();
position = m;
return this;
}
//重置mark为-1.position为0
public final Buffer rewind() {
position = 0;
mark = -1;
return this;
}使用案例
ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
buffer.putChar('a');
buffer.putChar('c');
System.out.println("插入完数据 " + buffer);
buffer.mark();// 记录mark的位置
buffer.position(30);// 设置的position一定要比mark大,否则mark无法重置
System.out.println("reset前 " + buffer);
buffer.reset();// 重置reset ,reset后的position=mark
System.out.println("reset后 " + buffer);
buffer.rewind();//清除标记,position变成0,mark变成-1
System.out.println("清除标记后 " + buffer);可以看到如下的运行结果:
插入完数据 java.nio.DirectByteBuffer[pos=4 lim=1024 cap=1024]
reset前 java.nio.DirectByteBuffer[pos=30 lim=1024 cap=1024]
reset后 java.nio.DirectByteBuffer[pos=4 lim=1024 cap=1024]
清除标记后 java.nio.DirectByteBuffer[pos=0 lim=1024 cap=1024]
剩余空间——remaing
remaing
则表示当前的剩余空间:
public final int remaining() {
return limit - position;
}
读写实践
写操作主要就是按照自己的数据类型,写入到直接内存中,注意每次写入数据的时候,position都会自动加上写入数据的长度,指向下一个该写入的起始位置:
下面看看如何写入一段byte[]或者字符串:
public static void test1() {
ByteBuffer buffer = ByteBuffer.allocateDirect(10);
byte[] data = {1,2};
buffer.put(data);
System.out.println("写byte[]后 " + buffer);
buffer.clear();//清空了
buffer.put("hello".getBytes());//5个byte
System.out.println("hello".getBytes().length);
System.out.println("写string后 " + buffer);
}
结果:
写byte[]后 java.nio.DirectByteBuffer[pos=2 lim=10 cap=10]
5
写string后 java.nio.DirectByteBuffer[pos=5 lim=10 cap=10]
读的时候,可以通过一个外部的byte[]
数组进行读取。由于没有找到直接操作直接内存的方法: 因此如果想在JVM应用中使用直接内存,需要申请一段堆中的空间,存放数据。
如果有更好的方法,还请留言。
ByteBuffer buffer = ByteBuffer.allocateDirect(10);
buffer.put(new byte[]{1,2,3,4});
System.out.println("刚写完数据 " +buffer);
buffer.flip();
System.out.println("flip之后 " +buffer);
byte[] target = new byte[buffer.limit()];
buffer.get(target);//自动读取target.length个数据
for(byte b : target){
System.out.println(b);
}
System.out.println("读取完数组 " +buffer);
输出为
刚写完数据 java.nio.DirectByteBuffer[pos=4 lim=10 cap=10]
flip之后 java.nio.DirectByteBuffer[pos=0 lim=4 cap=10]
1
2
3
4
读取完数组 java.nio.DirectByteBuffer[pos=4 lim=4 cap=10]
常用方法
上面的读写例子中,有几个常用的方法:
clear()
这个方法用于清除mark和position,还有limit的位置:
public final Buffer clear() {
position = 0;
limit = capacity;
mark = -1;
return this;
}
flip()
这个方法主要用于改变当前的Position为limit,主要是用于读取操作。
public final Buffer flip() {
limit = position;
position = 0;
mark = -1;
return this;
}
compact()
这个方法在读取一部分数据的时候比较常用。
它会把当前的Position移到0,然后position+1移到1。
public ByteBuffer compact() {
int pos = position();
int lim = limit();
assert (pos <= lim);
int rem = (pos <= lim ? lim - pos : 0); unsafe.copyMemory(ix(pos), ix(0), rem << 0);
position(rem);
limit(capacity());
discardMark();
return this;
}
比如一段空间内容为:
123456789
当position的位置在2时,调用compact方法,会变成:
345678989
isDirect()
这个方法用于判断是否是直接内存。如果是返回true,如果不是返回false。
rewind()
这个方法用于重置mark标记:
public final Buffer rewind() {
position = 0;
mark = -1;
return this;
}
堆外内存操作类ByteBuffer的更多相关文章
- Java堆外内存之五:堆外内存管理类ByteBuffer
本篇主要讲解如何使用直接内存(堆外内存),并按照下面的步骤进行说明: 相关背景-->读写操作-->关键属性-->读写实践-->扩展-->参考说明 希望对想使用直接内存的朋 ...
- Java堆外内存之二:堆外内存使用总结
目录: <堆外内存操作类ByteBuffer> <DirectBuffer> <Unsafe(java可直接操作内存(),挂起与恢复,CAS操作)> 有时候对内存进 ...
- Java堆外内存之四:直接使用Unsafe类操作堆外内存
在nio以前,是没有光明正大的做法的,有一个work around的办法是直接访问Unsafe类.如果你使用Eclipse,默认是不允许访问sun.misc下面的类的,你需要稍微修改一下,给Type ...
- JVM初探- 使用堆外内存减少Full GC
JVM初探-使用堆外内存减少Full GC 标签 : JVM 问题: 大部分主流互联网企业线上Server JVM选用了CMS收集器(如Taobao.LinkedIn.Vdian), 虽然CMS可与用 ...
- Java堆外内存之一:堆外内存场景介绍(对象池VS堆外内存)
最近经常有人问我在Java中使用堆外(off heap)内存的好处与用途何在.我想其他面临几样选择的人应该也会对这个答案感兴趣吧. 堆外内存其实并无特别之处.线程栈,应用程序代码,NIO缓存用的都是堆 ...
- Netty堆外内存泄漏排查,这一篇全讲清楚了
上篇文章介绍了Netty内存模型原理,由于Netty在使用不当会导致堆外内存泄漏,网上关于这方面的资料比较少,所以写下这篇文章,专门介绍排查Netty堆外内存相关的知识点,诊断工具,以及排查思路提供参 ...
- 一次完整的JVM堆外内存泄漏故障排查记录
前言 记录一次线上JVM堆外内存泄漏问题的排查过程与思路,其中夹带一些JVM内存分配机制以及常用的JVM问题排查指令和工具分享,希望对大家有所帮助. 在整个排查过程中,我也走了不少弯路,但是在文章中我 ...
- Java堆外内存的使用
堆外内存的回收见HeapByteBuffer和DirectByteBuffer以及回收DirectByteBuffer 基本类型长度 在Java中有很多的基本类型,比如: byte,一个字节是8位bi ...
- 【Spark篇】---Spark调优之代码调优,数据本地化调优,内存调优,SparkShuffle调优,Executor的堆外内存调优
一.前述 Spark中调优大致分为以下几种 ,代码调优,数据本地化,内存调优,SparkShuffle调优,调节Executor的堆外内存. 二.具体 1.代码调优 1.避免创建重复的RDD,尽 ...
随机推荐
- 如何通过pid快速找出进程的路径
[carlton@oc3408554812 Desktop]$ top top - 09:35:06 up 32 min, 2 users, load average: 1.49, 1.56, 1 ...
- 自动装配Bean
Spring提供了几种技巧,可以减少XML的配置数量: 自动装配(autowiring):可以减少<property>(setter注入)和<constructor-arg>( ...
- Jenkins快速上手
一.Jenkins下载安装 1.到官网下载jenkins.war包:http://jenkins-ci.org/ 2.安装方法有两种: a) 把下载下来的jenkins.war包放到文件夹下,如C:\ ...
- ubuntu下firefox浏览器flash player插件的安装
自从装了双系统后,ubuntu下的音乐软件只能选择网页播放器了,这无疑是需要播放插件的,这个插件就是falsh player. 当初使用usb启动盘安装的,在安装的过程中还会报找不到CD-rom的错, ...
- Psp个人软件开发软件需求分析及用例分析
一.需求分析 1. 业务需求 1.1 应用背景 开发项目进度计划总是那么不明确,延期经常出现,甚至无法给出一个相对比较明确的延迟时间.这样给市场的推广会带来很大的影响,不确定因素使得应对十分困难. ...
- Java中多态、抽象类和接口
1:final关键字(掌握) (1)是最终的意思,可以修饰类,方法,变量. (2)特点: A:它修饰的类,不能被继承. B:它修饰的方法,不能被重写. C:它修饰的变量,是一个常量. (3)面试相关: ...
- javascript之DOM篇二(操作)
一.创建DOM元素 createElement:document.createElement(' 所要创建的元素标签名'): <!DOCTYPE html><html>< ...
- WindowsPhone开发—— 使用手绘图片做景区导览地图
前些日子在做景区App遇到需求,使用手绘图片做一个简易的地图,支持放大缩小平移以及显示景点Mark,安卓上可以使用一个叫做“mAppWidget”的开源库来完成,WP上有人建议用ArcGIS,但是考虑 ...
- javascript eval函数解析json数据时为什加上圆括号eval("("+data+")")
原因很简单:因为在js中{}表示一个语句块(代码段),所有加上"()"表示表达式
- Animations功能(区别于Transitions)
CSS3实现动画: 1 Transitions:定义元素的某个属性从一个属性值平滑过渡到另一个属性值. Transitions属性的使用方法如下所示: transition: property | ...