作为一个JAVA程序员我们也应该知道计算机是何如运行的,计算机本身是如何完成工作的。其实我很后悔大二的时候没有好好学习那些计算机底层的知识,把大部分是时间花费在那些花拳绣腿上。
一、CPU的基本单位是线程,我们编写的程序通过进程或者线程请求CPU进行处理,首先CPU会分配处理的时间片,处理的时候将操作数传递给CPU,最后处理的结果输出到‘本地变量’中,这个‘本地变量’及时我们说常说的‘栈’(先进后出)。为了提升性能那些数据将会被Cache到缓存当中,我们知道缓存有一级缓存、二级缓存和三级缓存。容量最大的是三级缓存也就是几十兆。缓存既然与CPU交互最快为什么容量那么小?百度百科的解释是:“由于一级缓存的技术难度和制造成本最高,提高容量所带来的技术难度增加和成本增加非常大,所带来的性能提升却不明显,性价比很低”,而且如果缓存的容量过大,就像内存一样就会涉及到很复杂的算法进行管理。
我们知道JAVA之所以能够实现“一次编写,到处运行”是因为有一个强大的JVM,在程序运行的时候会通过java的虚指令来完成JAVA虚拟机中的数据和对象的一些操作,最后根据不同的OS将这些虚指令翻译成OS真正的指令。
二、现在大多数的计算机都是多核的,就像多个人去处理一项任务一样。那么问题来了,当一个请求来的时候CPU是怎么知道的呢?当发起请求的时候是哪个CPU进行处理?一份数据同时被多个CPU操作,如何通知其他CPU我做了什么?
对于第一个问题我的想法是类似与键盘一样,监听器不断的对键盘进行扫描监听,如果这样CPU会非常的繁忙并且效率不高,这时有了“中断技术”,有了它CPU不用自己去问有没有请求来,而是需要请求的处理的设备发送出一个“请求中断”信号,这时CPU便会放下手头的工作去去处理这个请求。完整的过程是:请求中断→响应中断→关闭中断→保留断点→中断源识别→保护现场→中断服务子程序→恢复现场→中断返回。
第二个问题我的想法是由一个请求处理CPU来控制所有的请求操作,这样的有一些弊端,比如:当请求量很大的时候这个CPU估计会吃不消。那么不同的CPU处理不同的区域,用划分区域的方法。这样也有弊端:不同区域之间可能会互相通信。
三、CPU处理的速度是非常快的,这里就出现了Cache Line,Cache 一行或者多行。举一个例子:如果要遍历一个二维数组 int[][] a=new int[5][10]; 有两种方法进行遍历我们常用的是第一维作为外循环,第二维作为内循环;第二种相反,那么两种方法效率是否一致呢? 我平时也是乱来,不知道其中与CPU读取时有怎样的差距。刚才说的Cache Line是连续读入相邻的内存,JAVA数组中分配内存是先分配第一维,然后再分配第二维子数组,例如:int[2][1]和int[3][1]是位于不同的一个数组上的,所以用Cache Line到Cache到CPU缓存中的时候会连续的读入多个,如果用第二中方法读入,数据在不同的数组中也就是在内存中的区域不同,那么Cache Line就不会发挥出他的作用,最后便会一个一个的读入,变得很慢。
四、当数据以及Cache到缓存中以后,如果多个CPU都需要对这个数据进行处理,怎样保持各个CPU的读写一致性?就有了一个MESI协议(缓存一致性协议)。多个CPU通过总线相互连接,每个CPU除了要处理自己的请求之外还要监听总线上其他CPU的读写操作,通过监听自己的Cache做相应的处理,形成了虚共享。MESI我也不是很清楚,只知道几个基本的规则,1.一个CPU加载内存是专用的;2.当另一个CPU也加载了同一块内存时,那么此时两个CPU持有的信息将处于共享状态。
跟CPU相关的还有并发操作,并发还没认真专学习过,一直觉得自己用不上,其实并发随处可见,只是很多框架都帮我们处理好了。下次再谈并发吧,写的很粗糙,主要还是梳理思路和分享一些重要的东西。