这一篇,我想分享下自己对于内存的一些基本知识的理解,当然,这篇文章也仅仅适用于新入门的Java开发人员,对于一些有经验的开发人员,可以不看,如果刷到了,请直接关闭页面就可以了。笔者写了很多年的Java代码,和写了很多年C语言的人相比,对于内存的理解,肯定要浅显很多。如果你是C语言开发者,本文对于你来说,简直就是脱了裤子放屁一样,觉的说的这不都是废话吗?哈哈一笑即可。
1、一些废话
一些废话还是要说,我们必须在一些概念上达成一致,首先这篇文章会特别的基础,其次这里说的内存,大部分情况下都是物理内存,不是虚拟内存(新手不知道虚拟内存的可以略过),这里讲的不少内容都是概念和理论性质的,少部分有实际的代码,主要是操作系统课程会讲到的内容。
如果你希望学到JVM内存管理的内容,那么你会失望。
如果你希望学习到程序开发的一些入门的内存知识,你会满意。
2、前菜:计算机存储设备
广义上讲的计算机的存储设备,一共有以下几种:
- CPU的寄存器
- 内存
- 各种磁盘(例如机械硬盘、固态硬盘、U盘、光盘)
这里我们主要讲内存,内存的容量单条都在4G 8G 16G了(2020年),寄存器和磁盘我们都不讲。
3、内存里的最小存储单元是字节
都知道计算机只能存储和计算2进制数据,都是0和1,毫无疑问,计算机内存中存储的都是0和1,那么这里有1个基本的概念,就是一个0或者一个1,叫做位,8位是1个字节。
那么这里我想说第一个知识点,内存里的最小存储单元是字节。
假设我们写了一行代码:
boolean a = false;
那么申请了几个字节呢?很显然用1个位就可以表示,但是实际是1个字节。
如果考虑到字节对齐,可能不仅仅是1个字节。如果你需要用1个位,实际上分配了1个字节的内存,这个就叫内存碎片,更精确的讲叫内部碎片。结论就是你不能申请少于1个字节的内存。
4、字节是有顺序的
继续刚才的话题,假设你写了如下一行代码:
long a = 0x12345678; // 转16进制之后是: 0x12345678
这个数字,转换为2进制,就是:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7YID7oud-1609726062205)(…/img/image-20210102150223271.png)]
那么实际这个数字,在内存里是怎么存储的呢?由于Java不能查看实际的内存内容,这里我们借用C语言的GDB工具来实现,具体的过程就不写了,感兴趣的话,需要自己研究下。
(gdb) x/32xb 0x7ffeefbffb08
0x7ffeefbffb08: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x7ffeefbffb10: 0x78 0x56 0x34 0x12 0x00 0x00 0x00 0x00
0x7ffeefbffb18: 0x25 0x10 0x01 0x00 0x00 0x00 0x00 0x00
0x7ffeefbffb20: 0x30 0xfb 0xbf 0xef 0xfe 0x7f 0x00 0x00
(gdb) x/32tb 0x7ffeefbffb08
0x7ffeefbffb08: 01111000 01010110 00110100 00010010 00000000 00000000 00000000 00000000
0x7ffeefbffb10: 01111000 01010110 00110100 00010010 00000000 00000000 00000000 00000000
0x7ffeefbffb18: 00100101 00010000 00000001 00000000 00000000 00000000 00000000 00000000
0x7ffeefbffb20: 00110000 11111011 10111111 11101111 11111110 01111111 00000000 00000000
我使用GDB查看了变量a的地址,然后查看了该地址的内存,可以看到低位的0x78,在内存的低地址,小端法。
1个int类型有4个字节,但是4个字节在内存中是有顺序的,如果低位的字节,在内存的低地址,这种存储方式就是小端,如果反过来,就是大端。
对于Java程序员来说,可以不用关心这个问题,基本上用不到这个知识的。
5、字节对齐
我们知道了内存中的最小单位是字节,那么很明显我们不能申请0.5字节或者1.5字节的内存长度,那么对于32位系统来说,还有1个要注意的地方是,大概率是4字节对齐的,什么意思呢?就是假设 一个类占3个字节,那么最终Java也会给你申请4个字节,这个就是字节对齐,为什么需要对齐呢?是因为32位系统的最大可以寻址2^32次方个字节,地址使用4字节的int表示的,所以访问的内存地址的物理地址,必须是4字节的整倍数,这样好寻址嘛,也很好理解。
我猜测,64系统大概率是8字节对齐的。
不过不用担心,Java虚拟机规范规定了int就是4字节,无论你是32位系统还是64位系统,所以还不需要关心这个事情,知道就可以了。所以有时候,我更倾向于把ArrayList的初始容量设置为16。
6、局部性原理
CPU访问存储器时,无论是存取指令还是存取数据,所访问的存储单元都趋于聚集在一个较小的连续区域中。这个原理对于内存来说,更是如此。紧凑的内存数据更有利于计算机一次性把所有的数据都取出。
7、程序需要加载进内存才能执行
这简直就是脱了裤子放屁了,大家都知道。
所以有时候需要尽量把代码写的短一点,可能有一点点的好处,也完全可以忽略,毕竟不知道编译之后的目标代码到底怎么样。
8、虚拟内存
大部分情况下,程序员可以访问到的内存,都是虚拟内存,如果你的程序代码比你的内存还要大,直观的感受是这程序肯定不能运行,得益于虚拟内存,可以只加载一部分内存,然后按需加载。
全文完。
关注我的博客,获取更多Java编程知识: 双King的技术博客