JAVA相关的一些底层知识

1.计算机五大核心组成部分

1. 控制器(Control):是整个计算机的中枢神经,其功能是对程序规定的控制信息进行解释,根据其要求进行控制,调度程序、数据、地址,协调计算机各部分工作及内存与外设的访问等。
​
2. 运算器(Datapath):运算器的功能是对数据进行各种算术运算和逻辑运算,即对数据进行加工处理。 CPU
​
3. 存储器(Memory):存储器的功能是存储程序、数据和各种信号、命令等信息,并在需要时提供这些信息。内存
​
4. 输入(Input system):输入设备是计算机的重要组成部分,输入设备与输出设备合你为外部设备,简称外设,输入设备的作用是将程序、原始数据、文字、字符、控制命令或现场采集的数据等信息输入到计算机。常见的输入设备有键盘、鼠标器、光电输入机、磁带机、磁盘机、光盘机等。
​
5. 输出(Output system):输出设备与输入设备同样是计算机的重要组成部分,它把外算机的中间结果或最后结果、机内的各种数据符号及文字或各种控制信号等信息输出出来。微机常用的输出设备有显示终端CRT、打印机、激光印字机、绘图仪及磁带、光盘机等。

上面的只是计算机的逻辑模型,生产出来的计算机是下面的模型

cpu和内存是上面的重点
cpu和内存在不同的插槽上,他们想要共同协作需要经过总线

2.CPU指令结构

CPU内部结构

  • 控制单元  运算单元  数据单元  三部分

控制单元:
CPU是硬件,想要操作硬件就要使用0,1进行操作,但是0,1对于处理太复杂了,于是各种CPU厂商就将01封装成汇编语言,我们外部直接调用内置的汇编语言就能被CPU识别

运算单元:
运算单元是运算器的核心。可以执行算术运算(包括加减乘数等基本运算及其附加运算)和逻辑运算(包括移位、逻辑测试或两个值比较)

数据单元:
CPU 中暂时存放数据的地方,包含寄存器和缓存,里面保存着那些等待处理的数据,或已经处理过的数据,CPU 访问寄存器所用的时间要比访问内存的时间短。寄存器是CPU内部的元件,寄存器拥有非常高的读写速度,所以在寄存器之间的数据传送非常快
内存结构

但开启一个进程以后,为这个进程开辟一个空间,这个空间由指令段和数据段组成,指令段负责存储指令,由cpu的指令寄存器从指令段中拿指令
数据段,存放数据,由存储单元的寄存器中中取数据
内存用来存放大量等待执行的数据和指令,CPU中的指令寄存器和数据寄存器空间很小,用来存放正在进行的数据和指令以及缓存非常小的要进行的

3.CPU缓存结构

现代CPU为了提升执行效率,减少CPU与内存的交互(交互影响CPU效率),一般在CPU上集成了多级缓存架构,常见的为三级缓存结构

  • L1 Cache,分为数据缓存和指令缓存,逻辑核独占

  • L2 Cache,物理核独占,逻辑核共享

  • L3 Cache,所有物理核共享

存储器存储空间大小:内存>L3>L2>L1>寄存器;
存储器速度快慢排序:寄存器>L1>L2>L3>内存;
还有一点值得注意的是:缓存是由最小的存储区块-缓存行(cacheline)组成,缓存行大小通常为64byte。
缓存行是什么意思呢?
比如你的L1缓存大小是512kb,而cacheline = 64byte,那么就是L1里有512 * 1024/64个cacheline
L3是内核共享,L1和L2是每个内核独享的,这种设计是因为硬件无法满足,不然L1设计很大就可以了
L1也分为两种,一个是指令的缓存,一个是数据的缓存

1.CPU读取存储器数据过程

1、CPU要取寄存器X的值,只需要一步:直接读取。
​
2、CPU要取L1 cache的某个值,需要1-3步(或者更多):把cache行锁住,把某个数据拿来,解锁,如果没锁住就慢了。
​
3、CPU要取L2 cache的某个值,先要到L1 cache里取,L1当中不存在,在L2里,L2开始加锁,加锁以后,把L2里的数据复制到L1,再执行读L1的过程,上面的3步,再解锁。
​
4、CPU取L3 cache的也是一样,只不过先由L3复制到L2,从L2复制到L1,从L1到CPU。
​
5、CPU取内存则最复杂:通知内存控制器占用总线带宽,通知内存加锁,发起内存读请求,等待回应,回应数据保存到L3(如果没有就到L2),再从L3/2到L1,再从L1到CPU,之后解除总线锁定。

2.CPU为何要有高速缓存

CPU在摩尔定律的指导下以每18个月翻一番的速度在发展,然而内存和硬盘的发展速度远远不及CPU。这就造成了高性能能的内存和硬盘价格及其昂贵。然而CPU的高度运算需要高速的数据。为了解决这个问题,CPU厂商在CPU中内置了少量的高速缓存以解决I/O速度和CPU运算速度之间的不匹配问题。

在CPU访问存储设备时,无论是存取数据抑或存取指令,都趋于聚集在一片连续的区域中,这就被称为局部性原理。

时间局部性(Temporal Locality):如果一个信息项正在被访问,那么在近期它很可能还会被再次访问。

  • 比如循环、递归、方法的反复调用等。 所以要设计多层,L1用完了,L2的或者L3的不会被立即清理掉,有可能一会还用

空间局部性(Spatial Locality):如果一个存储器的位置被引用,那么将来他附近的位置也会被引用。

  • 比如顺序执行的代码、连续创建的两个对象、数组等。

综上:  
缓存行首先为了解决内存慢
设计多层缓存的原因:  为了时间局部性
缓存哪些数据: 空间局部性,访问数据临近的
//空间局部性实践结果
//二维数组计算大小
//按行计算的速度比按列计算快
public class TwoDimensionalArraySum {
    private static final int RUNS = 100;
    private static final int DIMENSION_1 = 1024 * 1024;
    private static final int DIMENSION_2 = 6;
    private static long[][] longs;
​
    public static void main(String[] args) throws Exception {
        /*
         * 初始化数组
         */
        longs = new long[DIMENSION_1][];
        for (int i = 0; i < DIMENSION_1; i++) {
            longs[i] = new long[DIMENSION_2];
            for (int j = 0; j < DIMENSION_2; j++) {
                longs[i][j] = 1L;
            }
        }
        System.out.println("Array初始化完毕....");
​
        long sum = 0L;
        long start = System.currentTimeMillis();
        for (int r = 0; r < RUNS; r++) {
            for (int i = 0; i < DIMENSION_1; i++) {//DIMENSION_1=1024*1024
                for (int j=0;j<DIMENSION_2;j++){//6
                    sum+=longs[i][j];
                }
            }
        }
        System.out.println("spend time1:"+(System.currentTimeMillis()-start));
        System.out.println("sum1:"+sum);
​
        sum = 0L;
        start = System.currentTimeMillis();
        for (int r = 0; r < RUNS; r++) {
            for (int j=0;j<DIMENSION_2;j++) {//6
                for (int i = 0; i < DIMENSION_1; i++){//1024*1024
                    sum+=longs[i][j];
                }
            }
        }
        System.out.println("spend time2:"+(System.currentTimeMillis()-start));
        System.out.println("sum2:"+sum);
    }
}            

3.带有高速缓存的CPU执行计算的流程

1. 程序以及数据被加载到主内存
2. 指令和数据被加载到CPU的高速缓存
3. CPU执行指令,把结果写到高速缓存
4. 高速缓存中的数据写回主内存

4.CPU运行安全等级

CPU有4个运行级别,分别为:ring0 ring1 ring2 ring3
Linux与Windows只用到了2个级别:ring0、ring3,操作系统内部内部程序指令通常运行在ring0级别,操作系统以外的第三方程序运行在ring3级别,第三方程序如果要调用操作系统内部函数功能,由于运行安全级别不够,必须切换CPU运行状态,从ring3切换到ring0,然后执行系统函数,为什么JVM创建线程,线程阻塞唤醒是重型操作了,因为CPU要切换运行状态。
​
step1:CPU从ring3切换ring0创建线程
step2:创建完毕,CPU从ring0切换回ring3
step3:线程执行JVM程序
step4:线程执行完毕,销毁还得切回ring0
我们的JAVA指令,要在cpu上运行,一开始指令的运行级别是ring3,cpu可以识别到哪些指令是ring0级别才能运行的,当运行到一些调用内核的命令时,就会检测到命令的运行级别不够,切换cpu运行级别,切换以后开始执行调用内核的指令

这里联合下面的操作系统内存管理大概就能明确一点,指令存放的位置不一样,所需要执行的cpu运行级别也会不一样

4.CPU核心与线程的关系

一个计算机可以安装多个CPU,这个要看计算机的主板设计,笔记本一般就一个
​
一个CPU有多个核心: 提高并行处理能力每个核心可以处理一个线程    当然也可以多个CPU,每个单核,但是成本非常高
​
多核超线程:  通过这个技术,一个核心可以同时处理两个线程,这也就是我们的物理内核和逻辑内核,逻辑内核就是超线程技术模拟出来的
            但是这种超线程的逻辑核并不是什么线程都能执行,某些特定的线程才能够执行

5.操作系统内存管理

操作系统有用户空间与内核空间两个概念,目的也是为了做到程序运行安全隔离与稳定
​
cpu运行用户空间的指令,发现需要调用内核空间的指令,比如创建线程等,首先cpu要切换运行状态,然后去访问内核空间的命令

内核线程(KLT):系统内核管理线程(KLT),内核保存线程的状态和上下文信息,线程阻塞不会引起进程阻塞。在多处理器系统上,多线程在多处理器上并行运行。线程的创建、调度和管理由内核完成,效率比ULT要慢,比进程操作快。 
java使用KLT
​
用户线程(ULT):用户程序实现,不依赖操作系统核心,应用提供创建、同步、调度和管理线程的函数来控制用户线程。不需要用户态/内核态切换,速度快。内核对ULT无感知,线程阻塞则进程(包括它的所有线程)阻塞。

6.虚拟机指令集架构


虚拟机指令集架构主要分两种:

1、栈指令集架构

2、寄存器指令集架构

关于指令集架构的wiki详细说明:https://zh.wikipedia.org/wiki/%E6%8C%87%E4%BB%A4%E9%9B%86%E6%9E%B6%E6%A7%8B


栈指令集架构

1. 设计和实现更简单,适用于资源受限的系统;
2. 避开了寄存器的分配难题:使用零地址指令方式分配;
3. 指令流中的指令大部分是零地址指令,其执行过程依赖与操作栈,指令集更小,编译器容易实现;
4. 不需要硬件支持,可移植性更好,更好实现跨平台。

寄存器指令集架构

1. 典型的应用是x86的二进制指令集:比如传统的PC以及Android的Davlik虚拟机。
2. 指令集架构则完全依赖硬件,可移植性差。
3. 性能优秀和执行更高效。
4. 花费更少的指令去完成一项操作。
5. 在大部分情况下,基于寄存器架构的指令集往往都以一地址指令、二地址指令和三地址指令为主,而基于栈式架构的指令集却是以零地址指令为主。
Java符合典型的栈指令集架构特征,像Python、Go都属于这种架构。课上将给大家剖析整个栈指令集架构执行链路过程。
​
栈指令集架构速度比较慢,数据在内存中的jvm栈空间中,变量信息放在栈的局部变量表中,在JVM学习的时候,一个a+b的操作先要从局部变量表中弹出数字,加入到操作数栈,操作数栈中的数要交给CPU去运行,由前面的缓存模型可知,数据到达cpu运算还是一个缓慢的过程,等cpu算完再放回操作数栈,操作数栈再将数据复制到局部变量表中,由此可见这么多步骤能不慢吗
​
但是为了可一至性,也只能使用栈指令集架构

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值