热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

java堆内存分析

数据类型下面这些,都是在使用DirectBuffer中必备的一些常识,暂作了解吧!如果想要深入理解,可以看看下面参考的那些博客。基本类型长度在Java中有很多的基本类型,比如:byte,一个字节是8位

数据类型

下面这些,都是在使用DirectBuffer中必备的一些常识,暂作了解吧!如果想要深入理解,可以看看下面参考的那些博客。

基本类型长度

在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也是如此。

大端小端

由于一个数据类型可能有很多个字节组成的,那么它们是如何摆放的。这个是有讲究的:

  • 大端:低地址位 存放 高有效字节

  • 小端:低地址位 存放 低有效字节

举个例子,一个char是有两个字节组成的,这两个字节存储可能会显示成如下的模样,比如字符a:

              低地址位    高地址位

大端;        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的数组,因此插入和读取都跟普通的数组差不多。

只不过提供了基于不同数据类型的插入方法,比如:

  • put(byte) 插入一个byte

  • put(byte[]) 插入一个byte数组

  • putChar(char) 插入字符

  • putInt(int) 插入Int

  • putLong(long) 插入long

等等….详细的使用方法,也可以参考下面的图片:

对应读取数据,跟写入差不多:

注意所有没有index参数的方法,都是按照当前position的位置进行操作的。

下面看看什么是position,还有什么其他的属性吧!

基本的属性值

它有几个关键的指标:

mark-->position-->limit-->capacity

另外,还有remaining=limit-position。

先说说他们的意思吧!

当前位置——position

position是当前数组的指针,指示当前数据位置。举个例子:

[java] view plain copy print?在CODE上查看代码片派生到我的代码片
  1. ByteBuffer buffer = ByteBuffer.allocateDirect(1024);  
  2. buffer.putChar('a');  
  3. System.out.println(buffer);  
  4. buffer.putChar('c');  
  5. System.out.println(buffer);  
  6. buffer.putInt(10);  
  7. 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)设置。

[java] view plain copy print?在CODE上查看代码片派生到我的代码片
  1. //position(int)方法的源码  
  2. public final Buffer position(int newPosition) {  
  3.         if ((newPosition > limit) || (newPosition < 0))  
  4.             throw new IllegalArgumentException();  
  5.         position = newPosition;  
  6.         if (mark > position) mark = -1;  
  7.         return this;  
  8.     }  

注意:position的位置要比limit小,比mark大

空间容量——capacity

capacity是当前申请的直接内存的容量,它是申请后就不会改变的。

capacity则可以通过capacity()方法获得。

限制大小——limit

我们可能想要改变这段直接内存的大小,因此可以通过一个叫做Limit的属性设置。

limit则可以通过limit()获得,通过limit(int)进行设置。

注意limit要比mark和position大,比capacity小。

[java] view plain copy print?在CODE上查看代码片派生到我的代码片
  1. //limit(int)方法的源码  
  2. public final Buffer limit(int newLimit) {  
  3.         if ((newLimit > capacity) || (newLimit < 0))  
  4.             throw new IllegalArgumentException();  
  5.         limit = newLimit;  
  6.         if (position > limit) position = limit;  
  7.         if (mark > limit) mark = -1;  
  8.         return this;  
  9.     }  

标记位置——mark

mark,就是一个标记为而已,记录当前的position的值。常用的场景,就是记录某一次插入数据的位置,方便下一次进行回溯。

  • 可以使用mark()方法进行标记,

  • 使用reset()方法进行清除,

  • 使用rewind()方法进行初始化

[java] view plain copy print?在CODE上查看代码片派生到我的代码片
  1. //mark方法标记当前的position,默认为-1  
  2. public final Buffer mark() {  
  3. mark = position;  
  4. return this;  
  5. }  
  6. //reset方法重置mark的位置,position的位置,不能小于mark的位置,否则会出错  
  7. public final Buffer reset() {  
  8. int m = mark;  
  9. if (m < 0)  
  10.     throw new InvalidMarkException();  
  11. position = m;  
  12. return this;  
  13. }  
  14. //重置mark为-1.position为0  
  15. public final Buffer rewind() {  
  16. position = 0;  
  17. mark = -1;  
  18. return this;  
  19. }  

使用案例

[java] view plain copy print?在CODE上查看代码片派生到我的代码片
  1. ByteBuffer buffer = ByteBuffer.allocateDirect(1024);  
  2. buffer.putChar('a');  
  3. buffer.putChar('c');  
  4. System.out.println("插入完数据 " + buffer);  
  5. buffer.mark();// 记录mark的位置  
  6. buffer.position(30);// 设置的position一定要比mark大,否则mark无法重置  
  7. System.out.println("reset前 " + buffer);  
  8. buffer.reset();// 重置reset ,reset后的position=mark  
  9. System.out.println("reset后 " + buffer);  
  10. buffer.rewind();//清除标记,position变成0,mark变成-1  
  11. 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则表示当前的剩余空间:

[java] view plain copy print?在CODE上查看代码片派生到我的代码片
  1. public final int remaining() {  
  2.       return limit - position;  
  3.   }  

读写实践

写操作主要就是按照自己的数据类型,写入到直接内存中,注意每次写入数据的时候,position都会自动加上写入数据的长度,指向下一个该写入的起始位置:

下面看看如何写入一段byte[]或者字符串:

[java] view plain copy print?在CODE上查看代码片派生到我的代码片
  1. ByteBuffer buffer = ByteBuffer.allocateDirect(10);  
  2. byte[] data = {1,2};  
  3. buffer.put(data);  
  4. System.out.println("写byte[]后 " + buffer);  
  5. buffer.clear();  
  6. buffer.put("hello".getBytes());  
  7. System.out.println("写string后 " + buffer);  

输出的内容为:

写byte[]后 java.nio.DirectByteBuffer[pos=2 lim=10 cap=10]

写string后 java.nio.DirectByteBuffer[pos=5 lim=10 cap=10]

读的时候,可以通过一个外部的byte[]数组进行读取。由于没有找到直接操作直接内存的方法: 因此如果想在JVM应用中使用直接内存,需要申请一段堆中的空间,存放数据。

如果有更好的方法,还请留言。

[java] view plain copy print?在CODE上查看代码片派生到我的代码片
  1. ByteBuffer buffer = ByteBuffer.allocateDirect(10);  
  2. buffer.put(new byte[]{1,2,3,4});  
  3. System.out.println("刚写完数据 " +buffer);  
  4. buffer.flip();  
  5. System.out.println("flip之后 " +buffer);  
  6. byte[] target = new byte[buffer.limit()];  
  7. buffer.get(target);//自动读取target.length个数据  
  8. for(byte b : target){  
  9.     System.out.println(b);  
  10. }  

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的位置:

[java] view plain copy print?在CODE上查看代码片派生到我的代码片
  1. public final Buffer clear() {  
  2.         position = 0;  
  3.         limit = capacity;  
  4.         mark = -1;  
  5.         return this;  
  6.     }  

flip()

这个方法主要用于改变当前的Position为limit,主要是用于读取操作。

[java] view plain copy print?在CODE上查看代码片派生到我的代码片
  1. public final Buffer flip() {  
  2.      limit = position;  
  3.      position = 0;  
  4.      mark = -1;  
  5.      return this;  
  6.  }  

compact()

这个方法在读取一部分数据的时候比较常用。

它会把当前的Position移到0,然后position+1移到1。

[java] view plain copy print?在CODE上查看代码片派生到我的代码片
  1. public ByteBuffer compact() {  
  2.     int pos = position();  
  3.     int lim = limit();  
  4.     assert (pos <= lim);  
  5.     int rem = (pos <= lim ? lim - pos : 0);  
  6.     unsafe.copyMemory(ix(pos), ix(0), rem << 0);  
  7.     position(rem);  
  8.     limit(capacity());  
  9.     discardMark();  
  10.     return this;  
  11. }  

比如一段空间内容为:

123456789

当position的位置在2时,调用compact方法,会变成:

345678989

isDirect()

这个方法用于判断是否是直接内存。如果是返回true,如果不是返回false。

rewind()

这个方法用于重置mark标记:

[java] view plain copy print?在CODE上查看代码片派生到我的代码片
  1. public final Buffer rewind() {  
  2.        position = 0;  
  3.        mark = -1;  
  4.        return this;  
  5.    }  

推荐阅读
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • Nginx使用(server参数配置)
    本文介绍了Nginx的使用,重点讲解了server参数配置,包括端口号、主机名、根目录等内容。同时,还介绍了Nginx的反向代理功能。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • 本文介绍了Oracle数据库中tnsnames.ora文件的作用和配置方法。tnsnames.ora文件在数据库启动过程中会被读取,用于解析LOCAL_LISTENER,并且与侦听无关。文章还提供了配置LOCAL_LISTENER和1522端口的示例,并展示了listener.ora文件的内容。 ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • 本文讨论了clone的fork与pthread_create创建线程的不同之处。进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合。在调用系统调用fork创建一个进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性。但是二者之间的通讯需要通过专门的通讯机制,另外通过fork创建子进程系统开销很大。因此,在某些情况下,使用clone或pthread_create创建线程可能更加高效。 ... [详细]
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
  • 配置IPv4静态路由实现企业网内不同网段用户互访
    本文介绍了通过配置IPv4静态路由实现企业网内不同网段用户互访的方法。首先需要配置接口的链路层协议参数和IP地址,使相邻节点网络层可达。然后按照静态路由组网图的操作步骤,配置静态路由。这样任意两台主机之间都能够互通。 ... [详细]
  • 海马s5近光灯能否直接更换为H7?
    本文主要介绍了海马s5车型的近光灯是否可以直接更换为H7灯泡,并提供了完整的教程下载地址。此外,还详细讲解了DSP功能函数中的数据拷贝、数据填充和浮点数转换为定点数的相关内容。 ... [详细]
  • 本文介绍了Swing组件的用法,重点讲解了图标接口的定义和创建方法。图标接口用来将图标与各种组件相关联,可以是简单的绘画或使用磁盘上的GIF格式图像。文章详细介绍了图标接口的属性和绘制方法,并给出了一个菱形图标的实现示例。该示例可以配置图标的尺寸、颜色和填充状态。 ... [详细]
  • Android工程师面试准备及设计模式使用场景
    本文介绍了Android工程师面试准备的经验,包括面试流程和重点准备内容。同时,还介绍了建造者模式的使用场景,以及在Android开发中的具体应用。 ... [详细]
  • 本文介绍了闭包的定义和运转机制,重点解释了闭包如何能够接触外部函数的作用域中的变量。通过词法作用域的查找规则,闭包可以访问外部函数的作用域。同时还提到了闭包的作用和影响。 ... [详细]
  • 本文介绍了解决Netty拆包粘包问题的一种方法——使用特殊结束符。在通讯过程中,客户端和服务器协商定义一个特殊的分隔符号,只要没有发送分隔符号,就代表一条数据没有结束。文章还提供了服务端的示例代码。 ... [详细]
  • 目录实现效果:实现环境实现方法一:基本思路主要代码JavaScript代码总结方法二主要代码总结方法三基本思路主要代码JavaScriptHTML总结实 ... [详细]
  • 猜字母游戏
    猜字母游戏猜字母游戏——设计数据结构猜字母游戏——设计程序结构猜字母游戏——实现字母生成方法猜字母游戏——实现字母检测方法猜字母游戏——实现主方法1猜字母游戏——设计数据结构1.1 ... [详细]
author-avatar
芦子根_889
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有