内存是什么?
Posted 小智RE0
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了内存是什么?相关的知识,希望对你有一定的参考价值。
最近在大佬的视频中学习内存知识:
视频地址【计算机知识串讲】从下到上看内存
文章目录
1.内存条,总线,以及DMA 认识
内存条:
CPU与内存条之间使用了 数据总线
与地址总线
进行连接使用;
总线还有IO总线,控制总线,局部总线,PCIE总线…
IO总线
:比如USB
,也就是通用串行总线
,
PCIE总线
:比如使用的显卡就是通过PCIE总线与CPU进行交互连接;
在散热板下还有一部分即南桥
;
这个南桥
会接入DMA控制芯片
;
这里DMA的知识, 在学习IO多路复用知识
时之前有提到过;
这里CPU为DMA下达指令时,也为他发放了使用总线的权限
; 他们之间是轮流使用总线的控制权;像是一种隔离关系.
2.内存管理与分配
操作系统的内存管理
逻辑地址:也就是程序自身看到的内存空间,作为一个抽象的地址;逻辑内存
需要映射到物理内存
上以后才能完成对内存的操作.
那么为什么程序操作的是逻辑内存地址, 而不是直接操作物理地址呢?或者直接对内存条操作?
- 由于程序无法知道真正的物理地址,那么就需要进行映射;
- 程序硬件可用的地址时变化的,由于涉及到进程的操作;
- 除非使用的是单进程的机器,否则都有可能因为进程变化出现安全问题;
逻辑地址与物理地址映射处理
(1)首先看这种简单的基于偏移量的映射
;
虽然比较便捷,但是存在着安全问题:
程序使用的内存是无法固定计算的,在运行使用的过程中会发生变化;
- 假如程序实际使用的内存比这个预算的偏移量还要小,那么也就是说有很多内存没有被完全利用,也就是产生的
内碎片问题
; - 假如程序运行之后,空间被释放;但是下一段程序需要使用的连续空间比这块空闲的空间还大一些,那么就无法使用,长期无法使用的闲置内存也就产生了
外碎片问题
.
(2)分页式的内存映射关系;
逻辑内存与物理内存之间采用页表的映射关系作为中介,查找对应的关系.
当然,不同的进程之间肯定不能使用同一个页表进行操作;
- 内存中的一个地址存放的数据量为一个字节[byte];
- 32位的操作系统物理地址个数为232 , 所以仅使用4GB内存;
- 多个程序使用的内存总和会大于物理内存,那么就需要借助于磁盘来进行存储,不经常使用的内存就会先存入到磁盘,然后
页表
对应的帧号显示时记录的是磁盘. - 采用了分页之后,每个程序都可以拥有一块很大的逻辑空间,通过映射磁盘与相应的置换算法,使得内存可用空间变大;
- 分页时对不同的进行保持内存隔离,降低了内存碎片问题.
内存案例分析:
机器 : 32位系统 , 256MB内存 ,4KB大小的页;
程序: 32位程序;
4K = 12Bit (212)
逻辑地址: 32bit = 20bit页号 + 12bit偏移
物理地址: 28bit = 16bit帧号 + 12bit偏移
0x000011a3
那么即可拆分为 页号 : 00001 + 偏移量: 1a3
实际上,对于这样简单的分页机制,还可以用TLB快表
进行时间上的优化:
将常用的几个页表项
(8-128个) 存入到访问速度较快的硬件中,比如:MMU内存管理单元
.
先进行
寻址查找到TLB快表
,然后再查询PT页表
,这个TLB快表的命中率非常高,因为程序常访问的页面并不会太多
程序内部的内存管理实际使用了一种逻辑上的分段机制;
malloc若申请了大于128KB的内存时,就会调用mmap,在堆和栈之间的区域申请内存,
也就是此处的lib位置,由于都是页,映射磁盘即文件映射内存时使用的系统调用
对于分段和分页结合的方式:
一个段分为多个页,在页表
中存储了页号.段号
的唯一映射的物理地址帧号;
由于虚拟地址有时候会比真实的物理地址还要大,那么就可以将 虚拟地址的页号
直接映射对应磁盘
;
那么在之后使用时,由于该页号对应的是磁盘,那么就有可能出现缺页中断的问题.
在Linux中 使用了一块swap区域作为磁盘的交换分区.
对于缓冲缓存理解:
page页级别的缓存:
以
Page
作为单位,缓存文件内容,缓存在Page Cache
中的文件数据,可以快速被用户读取,且对于使用了buffer缓冲
的写操作,数据在写入到Page Cache
中时即可立刻返回,无需等待数据被持久化到磁盘中,提升了上层应用读写文件的性能.
Buffer缓冲区级别的缓存:
- 在磁盘中的最小数据单位是
sector
,每次读写磁盘都是以sector
作为单位对磁盘进行操作,注意sector
和实际的磁盘类型相关.- 无论用户期望读取的大小是多少,在最后访问磁盘时,都会以
sector
作为单位进行读取;- 假如直接裸读磁盘,那么对于数据的读取效率是比较差的,若用户期望向磁盘中的某位置写操作一个byte的数据,也必须刷新一个
sector
,那么在写入这1byte之前,需要将1byte数据所在的磁盘sector
数据都给读取到,然后在内存中,修改对应的数据,然后再将整个修改结束的sector
数据全部写入到磁盘中;- 为提升磁盘的访问性能,那么实际上在内核会为磁盘的
sector
构建一层缓存,以sector
的整倍数力度为单位,在内存中缓存部分sector
数据,当出现读取数据的请求时,可以直接从内存中读取对应的数据,也可以直接从内存中更新指定部分的数据,然后采用异步
,将更新的数据写入对应磁盘的sector
中.
可查看windows系统下使用的内存情况;这里已提交就是申请的内存量大小,
3.内存相关的系统调用
系统调用也就是用户态切换内核态,申请内存时会用到。
例如:分次申请内存进行使用。
若改为仅申请一个字节,然后将指针强转为int类型,
first+1 向后移动4个字节;
实际上申请了一页的内存;
若是向后移动4096个字节;但是已经超出了该页的范围.,所以无法存储;
当myLock 了128K以上的内存时,就会触发
mmap
系统调用, 对应释放内存采用了munmap
;
参数: addr:起始地址, length:长度; prot: 权限标志符; flags: 指定映射对象的类型; fd: 文件描述符; offset: 偏移量;
这里fd设为-1,即直接申请内存; 申请的长度为100页;
注意申请之后不使用的话,无法得到该段内存;这里for循环使用到每一页的内存;
在查看进程时;由于每次使用该页时,会出现短暂的缺页,所以产生minflt
若直接将文件通过文件描述符映射到内存中时;也是惰性的,即不使用内存就不通过申请;
即文件并没有直接读到内存中;
监控进程时,发现有一个比较大的错误;要使用的这一页对应的是磁盘,发生缺页错误;
之后将文件全部加载到物理内存中,后面的错误是因为要将虚拟内存对应到物理内存中.
mmap
操作开始是在页表中存储页号对应磁盘地址
;
在真正读取时就会触发缺页操作
;将文件加载到内存中;
让用户空间与内核空间共享内存, 也就是 mmap操作节省了用户态与内核态之间的转换过程;[减少了内核空间->用户空间的拷贝];
4.Java中的内存
C++中的Klass使用java_mirror
指针对应 Java中的Class对象
对象引用一般是指4个字节[开启了指针压缩
];若未开启指针压缩则指的是8个字节;
- 在Java中的对象是
8字节对齐的
,那么即可使用32bit地址 表示 232 *8Byte = 32GB的内存地址;即使用了普通对象指针压缩
. 如果开启压缩,那么元数据区
就会为单独开出一部分压缩空间;这部分空间是连续的4GB大小,- 在堆内存中32G以内的都是默认开启指针压缩,每个对象地址使用4个字节表示,但
超出32G就无法开启压缩
,每个对象地址需要使用8字节表示.
开启/不开启指针压缩
的区别;
开启后 出现压缩空间与非类空间;
案例,申请1GB内存的场景
以上是关于内存是什么?的主要内容,如果未能解决你的问题,请参考以下文章