CPU是如何执行程序的?
冯诺依曼模型 5个部分 : 运算器,控制器,存储器,输入设备,输出设备
控制总线,数据总线,地址总线
32位的cpu可以处理4个字节 32bit的数据,通常称为cpu的位宽
64位的cpu可以处理8个字节 64bit的数据
线路位宽:就是地址总线的条数,每一条可以控制一个bit位的内存地址 32条就是2^32个内存地址
程序的执行流程:指令寄存器通过程序寄存器拿到指令的内存地址,然后从内存中拿出指令,进行解析,然后根据类型,交给逻辑计算器进行计算处理,或者交给控制单元进行存储
存储器的层次结构
CPU(L1,L2,L3(共享)) -> 内存 -> 磁盘
MESI协议
1.当 A 号 CPU 核心从内存读取变量 i 的值,数据被缓存在 A 号 CPU 核心自己的 Cache 里面,此时其他 CPU 核心的 Cache 没有缓存该数据,于是标记 Cache Line 状态为「独占」,此时其 Cache 中的数据与内存是一致的;
2.然后 B 号 CPU 核心也从内存读取了变量 i 的值,此时会发送消息给其他 CPU 核心,由于 A 号 CPU 核心已经缓存了该数据,所以会把数据返回给 B 号 CPU 核心。在这个时候, A 和 B 核心缓存了相同的数据,Cache Line 的状态就会变成「共享」,并且其 Cache 中的数据与内存也是一致的;
3.当 A 号 CPU 核心要修改 Cache 中 i 变量的值,发现数据对应的 Cache Line 的状态是共享状态,则要向所有的其他 CPU 核心广播一个请求,要求先把其他核心的 Cache 中对应的 Cache Line 标记为「无效」状态,然后 A 号 CPU 核心才更新 Cache 里面的数据,同时标记 Cache Line 为「已修改」状态,此时 Cache 中的数据就与内存不一致了。
4.如果 A 号 CPU 核心「继续」修改 Cache 中 i 变量的值,由于此时的 Cache Line 是「已修改」状态,因此不需要给其他 CPU 核心发送消息,直接更新数据即可。
5.如果 A 号 CPU 核心的 Cache 里的 i 变量对应的 Cache Line 要被「替换」,发现 Cache Line 状态是「已修改」状态,就会在替换前先把数据同步到内存。
tips:CPU Line中存放的数据,会根据tag标志来响应替换,脏数据的情况,读写都要通过总线告诉其他CPU我读取了当前内存的数据,你是否读取,我是否要等待你更新,我是否要更改状态
虚拟内存和内存分段
虚拟内存通过段表和段内偏移量来解决虚拟内存到物理内存的映射关系,物理内存被划分成多块segment。
段表记录的是每个段,对应的起始地址和段的大小限定,根据段选择因子中的段号(段表的索引)找到具体的段,然后根据段内偏移量 + 段的起始地址然后找到物理内存上的数据
分段为啥会导致内存碎片如何解决?
解决方法就是通过swap分区,将一些运行时的内存,先放到磁盘中,然后新创建的应用程序,跟着上面的应用程序紧接者分配,然后再把磁盘的数据读出来,此时stop the world
引出了内存分页
将内存分成一页一页,以页为单位为应用程序分配内存,G1的思想?通过页表映射
当内存不够用的时候,就会将最近没使用的内存页面释放掉,这里的释放掉是将应用程序信息写入磁盘,然后需要的时候再换进来,怎么有点像lru的思想
malloc内存分配
内存分配只是在虚拟内存中假分配标志,实际继续操作的时候,比如说将这个地址赋值,才会进入内核态,就是说查询页表的时候,会进行缺页中断,然后进入中断处理函数,等着内核处理内存分配,建立映射
对于 「malloc 申请的内存,free 释放内存会归还给操作系统吗?」这个问题,我们可以做个总结了:
malloc 通过 brk() 方式申请的内存,free 释放内存的时候,并不会把内存归还给操作系统,而是缓存在 malloc 的内存池中,待下次使用;
malloc 通过 mmap() 方式申请的内存,free 释放内存的时候,会把内存归还给操作系统,内存得到真正的释放。
brk是再用户内存中分配,mmap是在内核空间分配,所以要做内存映射
mmap方法内存分配的缺点:进入内核态
brk方法内存分配的优缺点:优点快,缺点内存碎片,因为是根据堆指针向上移动分配的,当然会回收到malloc缓冲区
回收内存的方法
一种是后台内存回收,也就是唤醒 kswapd 内核线程,这种方式是异步回收的,不会阻塞进程。
一种是直接内存回收,这种方式是同步回收的,会阻塞进程,这样就会造成很长时间的延迟,以及系统的 CPU 利用率会升高,最终引起系统负荷飙高。
进程崩溃和线程崩溃
其实是信号的传递,然后针对信号处理函数兜底,使得进程资源回收
jvm不会立刻崩溃,而是会抛异常是因为jvm自带了信号处理函数,顶替了操作系统底层的信号处理
访问内存有两种方式
- 直接访问(阻塞的) (DMA)
- 通过页缓存访问
大文件传输,推荐使用异步io + 直接访问,原因避免大文件浪费了page cache的资源,导致热点数据失效