Java堆外内存的使用
堆外内存的回收见HeapByteBuffer和DirectByteBuffer以及回收DirectByteBuffer
基本类型长度
在Java中有很多的基本类型,比如:
-
byte,一个字节是8位bit,也就是1B
-
short,16位bit,也就是2B
-
int,32位bit,也就是4B
-
long, 64位bit,也就是8B
-
char,16位bit,也就是2B
-
float,32位bit,也就是4B
-
double,64位bit,也就是8B
不同的类型都会按照自己的位数来存储,并且可以自动进行转换提升。byte、char、short都可以自动提升为int,如果操作数有long,就会自动提升为long,float和double也是如此。
大端小端
当前的存储器,多以byte为访问的最小单元,当一个逻辑上的地址必须分割为物理上的若干单元时就存在了先放谁后放谁的问题,于是端(endian)的问题应运而生了,对于不同的存储方法,就有大端(big-endian)和小端(little- endian)两个描述。
字节排序按分为大端和小端,概念如下
-
大端:低地址位 存放 高有效字节
-
小端:低地址位 存放 低有效字节
举个例子,一个char是有两个字节组成的,这两个字节存储可能会显示成如下的模样,比如字符a:
现在主流的CPU,intel系列的是采用的little endian的格式存放数据,而motorola系列的CPU采用的是big endian,ARM则同时支持 big和little,网络编程中,TCP/IP统一采用大端方式传送数据,所以有时我们也会把大端方式称之为网络字节序。特别需要注意的是,C/C++语言编写的程序里数据存储顺序是跟编译平台所在的CPU相关的,而 JAVA编写的程序则唯一采用big endian方式来存储数据。
举个例子说明,我的机子是32位windows的系统,处理器是AMD的。对于一个int型数0x12345678,为方便说明,这里采用16进制表示。这个数在不同字节顺序存储的CPU中储存顺序如下:
0x12345678 16进制,两个数就是一字节
高有效字节——>低有效字节: 12 34 56 78
低地址位 高低址位
大端: 12 34 56 78
小端: 78 56 34 12
读写数据
在直接内存中,通过allocateDirect(int byte_length)申请直接内存。这段内存可以理解为一段普通的基于Byte的数组,因此插入和读取都跟普通的数组差不多。
只不过提供了基于不同数据类型的插入方法,比如:
-
put(byte) 插入一个byte
-
put(byte[]) 插入一个byte数组
-
putChar(char) 插入字符
-
putInt(int) 插入Int
-
putLong(long) 插入long
等等。
基本的属性值
它有几个关键的指标:
mark-->position-->limit-->capacity
另外,还有remaining=limit-position。
当前位置——position,position是当前数组的指针,指示当前数据位置。position可以通过position()获得,也可以通过position(int)设置。注意:position的位置要比limit小,比mark大。
空间容量——capacity,capacity是当前申请的直接内存的容量,它是申请后就不会改变的。capacity则可以通过capacity()方法获得。
限制大小——limit,我们可能想要改变这段直接内存的大小,因此可以通过一个叫做Limit的属性设置。limit则可以通过limit()获得,通过limit(int)进行设置。注意limit要比mark和position大,比capacity小。
标记位置——mark, mark,就是一个标记为而已,记录当前的position的值。常用的场景,就是记录某一次插入数据的位置,方便下一次进行回溯。
-
可以使用mark()方法进行标记,mark方法标记当前的position,默认为-1
-
使用reset()方法进行清除,reset方法重置position的位置为mark的位置,mark不能小于0,否则会出错
-
使用rewind()方法进行初始化,重置mark为-1.position为0
使用案例
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则表示当前的剩余空间
常用方法
clear(),这个方法用于清除mark和position,还有limit的位置。
flip(),这个方法主要用于改变limit为当前的position,position为0,主要是用于读取操作。
compact(),这个方法在读取一部分数据的时候比较常用。它会把当前的Position移到0,然后position+1移到1,即类似substring(当前位置)的效果。
isDirect()这个方法用于判断是否是直接内存。如果是返回true,如果不是返回false。
rewind()这个方法用于重置mark标记。