全网最硬核Java程序员必备底层知识(一)

简介: 全网最硬核Java程序员必备底层知识(一)

一、引言

对于Java开发者而言,关于底层知识,我们一般当做黑盒来进行使用,不需要去打开这个黑盒。

但随着目前程序员行业的发展,我们有必要打开这个黑盒,去探索其中的奥妙。

本篇系列文章,将带你一起探索底层黑盒的奥秘之处。

二、相关书籍推荐

读书的原则:不求甚解,观其大略

67fbc9f779cd424eb8af46b1eda9a770.png


你如果进到庐山里头,二话不说,蹲下头来,弯下腰,就对着某棵树某棵小草猛研究而不是说先把庐山的整体脉络研究清楚了,那么你的学习方法肯定效率巨低而且特别痛苦。


最重要的还是慢慢地打击你的积极性,说我的学习怎么那么不happy啊,怎么那么没劲那,因为你的学习方法错了,大体读明白,先拿来用,用着用着,很多道理你就明白了。


《编码:隐匿在计算机软硬件背后的语言》

《深入理解计算机系统》(不建议读)

《算法导论》、《Java数据结构和算法》、《剑指offer》

《30天自制操作系统》

《TCP/IP详解》卷一

龙书《编译原理》

三、硬件基础知识

1、CPU的制作过程

CPU是如何制作的?

我相信每一个人都会都这么一个问号,今天来告诉你,所有的CPU都来源于:沙子

对于CPU的制作流程,这里有篇文章,大家有兴趣的可以看一下:CPU是如何制作的

没有兴趣的直接看概括:

  • 第一步:我们从沙子中提供单晶硅 晶体
  • 第二步:将晶体切割成薄片,得到 晶圆
  • 第三步:将金属粒子轰击到晶圆上,再进行 电镀
  • 第四步:在晶圆上进行 光刻,完成不同晶体管之间的导线互连
  • 第五步:质量检测,去除质量差的CPU

2、CPU的原理

计算机最开始要解决的问题:如何代表数字?

最原始的计算机采用的是利用灯泡,当我们计算 0100 + 1010 时,我们使用 8 个灯泡,以 灯泡的状态来代表 0 和 1,这样我么的第一版的计算机已经OK了。


ENIAC重达27吨,占地1800平方英尺(约合167.2平方米)。诞生于二战时期,最初是作为辅助炮兵计算炮弹轨迹的工具。



image.png

第一版的计算机,有一个让人哭笑不得的缺点。

我们在对计算机进行高速计算时,灯泡的闪光频次比较高,有可能造成灯泡的损坏,就需要有工作人员及时的更换灯泡,从而影响效率。

作为最初的一款计算机来说,他的面世足够让地球人震撼。

目前的计算机大都采取晶体管的方式进行计算,利用 与门或门非门或非门 的状态去表示不同的计算方式。

我们经常在生活中听说,CPU32位、CPU64位,简单来说,他们的区别就是:一次性读取多少位(bit)的数字

我们任何的计算,都可以通过逻辑运算来进行得到,我们来看下面这个逻辑运算:0 && 1 = 0,是怎么实现的?

首先,我们看一下电路图:这是一个 与门 电路图

对于该电路而言,AB 作为输入,Q 作为输出。

例如 A 输入低电平、B 输出高电平,那么 Q 就会输出低电平,转换为二进制就是 A 输入0、B 输出1,那么 Q 就会输出0,对应的逻辑运算表达式为 0 && 1 = 0

这里有一个关于BUG来源的小故事:从前有一个人,进行计算机的计算时,

发现数字总是不正确,找了好久也没找到原因,后来发现是计算机有个孔被虫子(BUG)腐蚀了,导致没办法进行低电平、高电平的切换,从此,我们编程上的错误就叫做:BUG

3、汇编语言的执行过程

我们想一下,在上面电路中,发生了 0 && 1 = 0 这样的事件,我使用者怎么知道机器发生了这种事件呢?

我们不可能直接把机器拆开,看里面的电平变换吧。

所以,在这里就出现了 汇编语言,而汇编语言的本质也是作为机器语言的助记符 出现的

比如,我们给计算机说,你去给我计算 1 + 2 这个操作,计算机需要进行高低电平的差异输出计算结果

而我们的汇编语言:movaddsub

我们可以一目了然的了解目前计算机的操作状态

计算机的组成图:



376d9e2f766840019682c153a99f80df.png

我们看一下计算机的计算的整个流程:

这里要说明一下 JavaC 语言的区别:

  • C语言:直接可以让CPU进行编译
  • Java语言:需要让 JVM 翻译,才能让 CPU 编译,这也正是 Java 跨平台的关键所在

4、量子计算机

对于量子计算机而言,目前世界上都在进行探索,暂无成果

在我们普通的计算机中,一个比特代表 1 或者 0,32个比特,可以代表 2^32 的任何一个数字

而我们的量子比特,最亮眼地方在于,他可以同时表示10

  • 一位量子比特:1、0
  • 二位量子比特:00、01、10、11
  • 三位量子比特:000、001、011、111…
  • 三十二位量子比特:一次性表示 2 ^ 32 的数字

这样描述可能不太直观,我们看一个例子:

现在有一个数字,我们知道该数字范围为: 1~2^32,我们怎么能快速求出该数字呢?

对于普通比特而言,一次只能表示一个,所以我们需要循环遍历 2^32 次,才可以找到该数字

而对于量子比特而言,直接使用 32 位的操作系统即可完成

5、CPU的基本组成

  • PC(Programme Counter):程序技术器当前指令的地址
  • Registers:寄存器,暂时存储CPU计算需要的数据
  • ALU(Arithmetic & logic Unit):运算单元,做运算使用
  • CU(Control Unit):控制单元
  • MMU(memory Mangagement unit):内存处理单元
  • Cache:缓存

5.1 ALU



2defa6627ac94f98b7c19eb31efdd225.png

之前的CPU属于单核情况,这样的话,会只有一个 Registers,我们的 PC 会不断的进行切换来指向新的线程,将所对应的数据存放到 Registers,对于切换(context switch)而言,会严重影响我们的效率。

现在的CPU通常是多核状态,在进行计算时,我们会有两个以上 Registers,这样的话,我们的PC就不需要频繁的进行切换,我们的 ALU 处理计算的切换即可。

5.2 寄存器

5.3 Cache

我们通过上面的图可以看出,我们的计算机为了获取数据的方便性,增加了三级缓存,对于不同的缓存,获取的时间的长短也是不一样的

对于多核CPU来说,如下图所示:

  • L1、L2存储在不同的核中
  • L3存储在同一个 CPU

5.3.1 局部性原理

简单来说,我们的CPU在读取数据时,将数据按快读取,不单独取一个字节,如下图所示:

当前的CPU需要 X 这个目标值,步骤如下:

  • 第一步:去寄存器里寻找,有没有 X 这个字段
  • 第二步:去 L1、L2、L3 Cache 去寻找 X 这个字段
  • 第三步:去内存、磁盘等寻找 X 这个字段
  • 第四步:找到后,将以 X 开头的 64个字节 形成一个块
  • 第五步:在 L3、L2、L1 中分别存入这个数据,方便下次去拿缓存
  • 第六步:将 X 写入寄存器,进行数据处理、

5.3.2 MESI Cache一致性协议

我们可以看到,对于上述两个核的 L1、L2 的缓存行要保持一致,保持一致的协议被称为:MESI 缓存一致性协议

CPU 每个 Cache line 标记四种状态

  • M(已修改):该 Cache line 有效,数据被修改了,和内存中的数据不一致,数据只存在于本Cache中。
  • E(独占):该 Cache line 有效,数据和内存中的数据一致,数据只存在于本Cache中。
  • S(共享):该 Cache line 有效,数据和内存中的数据一致,数据存在于很多Cache中。
  • I(无效):Cache line 是无效的

21016a382a914bac94ac3c8839e2e1e7.png

因特尔——缓存行

  • 缓存行越大,局部性空间效率越高,但读取时间慢
  • 缓存行越小,局部性空间效率越低,但读取时间快
  • 因特尔通过实验规定,缓存行的大小为:64 字节

总线锁(缓存行装不下的情况下,就必须锁总线)

缓存锁实现之一,有些无法被缓存的数据或者跨越多个缓存行的数据,依然必须使用总线锁

我们怎么测试我们的猜想是正确的呢?

我们测试两个程序

篇幅受限,源码的话这里暂时不展示了,有兴趣的可以关注公众号,回复:算法源码

  • 第一个程序:数值的更改在同一个缓存行
  • 第二个程序:数值的更改不在同一个缓存行



0335ebdf0c75456c8933197cd4288b8f.png

  • 有上述程序验证我们的猜想,两个线程频繁的更快缓存区中的缓存快,导致运行时间加长

5.3.3 缓存行对齐

对于有些特别敏感的数字,会存在线程高竞争的访问,为了保证不发生伪共享,我们一般不要求缓存行对齐

简单来说,我们不希望我们获取 X 数字的同时,把 Y 也给获取进来

在我们 JDK7disruptor 都采取long cache line padding

public long p1, p2, p3, p4, p5, p6, p7; // cache line padding
private volatile long cursor = INITIAL_CURSOR_VALUE;
public long p8, p9, p10, p11, p12, p13, p14; //cache line padding

这样的话,当我们缓存行获取时,就会把 cursor 前面的 long 或者 后面的 long 加载到缓存块中,避免 cursor 的缓存行对齐

在我们的 JDK8 中,我们可以给该参数加入 @Contended(根据底层的CPU来进行设定,保证不会让两个参数共享一个缓存行),需要加上 -XX:-RestrictContended 生效










相关文章
|
3月前
|
存储 安全 Java
Java修仙之路,十万字吐血整理全网最完整Java学习笔记(进阶篇)
本文是Java基础的进阶篇,对异常、集合、泛型、Java8新特性、I/O流等知识进行深入浅出的介绍,并附有对应的代码示例,重要的地方带有对性能、底层原理、源码的剖析。适合Java初学者。
Java修仙之路,十万字吐血整理全网最完整Java学习笔记(进阶篇)
|
7月前
|
IDE Java 程序员
Java程序员必备的21个核心技术,你都掌握了哪些?,深入浅出Java开发
Java程序员必备的21个核心技术,你都掌握了哪些?,深入浅出Java开发
|
7月前
|
监控 安全 Java
精彩推荐 |【Java技术专题】「重塑技术功底」攻破Java技术盲点之剖析动态代理的实现原理和开发指南(中)
经历了上一篇文章内容:《精彩推荐 |【Java技术专题】「重塑技术功底」攻破Java技术盲点之剖析动态代理的实现原理和开发指南(上)》,相信您对于Java原生的动态代理技术应该有了一定的认识和了解了,那么我们先来回顾一下对应的技术要点,看看您是否真正的认识了对应的技术原理了?
67 0
精彩推荐 |【Java技术专题】「重塑技术功底」攻破Java技术盲点之剖析动态代理的实现原理和开发指南(中)
|
7月前
|
设计模式 缓存 Java
精彩推荐 |【Java技术专题】「重塑技术功底」攻破Java技术盲点之剖析动态代理的实现原理和开发指南(上)
在Java编程中,动态代理的应用非常广泛。它被广泛应用于Spring AOP框架、Hibernate数据查询、测试框架的后端mock、RPC以及Java注解对象获取等领域。
75 0
精彩推荐 |【Java技术专题】「重塑技术功底」攻破Java技术盲点之剖析动态代理的实现原理和开发指南(上)
|
7月前
|
设计模式 Dubbo NoSQL
终于拿到了爆火全网的进一线大厂程序员必看的1700道java面试题
爆火全网的进一线大厂程序员必看的1700道java面试题到底有多牛? 牛不牛不敢说,但是有好多程序员是靠这一套1700道高频面试题,顺利收到很多大厂offer! 以至于,到现在为止,大厂都开始按照这一套1700道面试题来对程序员进行考核! 这1700道java面试题包含的内容有:java基础、JVM、多线程、MySQL、spring、springboot、springcloud、dubbo、mybatis、redis、网络IO、Linux、MQ、zookeeper、netty、设计模式、算法、大数据相关知识、项目方面;
|
7月前
|
存储 安全 Java
【Java基础】全网最详细 - 从入门到转行
【Java基础】全网最详细 - 从入门到转行
149 0
|
算法 Java 程序员
字节跳动技术总监编写Java程序员算法笔记,一书在手工作不愁
本书覆盖了近3年程序员面试笔试中超过98%的高频算法知识点当你细细品读完本书后,各类企业的offer将任由你挑选
|
设计模式 缓存 NoSQL
短期内跳槽的Java程序员必看的八项知识点+两大项目实战
8大知识模块 1:多线程高并发 2:JVM虚拟机 3:设计模式 4:redis 5: zookeeper 6: mysql调优课程 7:netty(网游后端项目) 8:spring源码分析
|
设计模式 缓存 算法
全网首发“Java面试考点大全”,20+互联网公司,应有尽有
受疫情影响,今年似乎给人感觉时间比往年还要流逝得更快。显然,春节一过,我们又将迎来面试旺季金三银四。对于程序员来说,秋招的失利更意味着在金三银四要打一场“硬战”,可又有多少人做好了面试的准备呢?对于一线互联网公司的面试,你又了解多少呢?
|
消息中间件 架构师 NoSQL
Java面试很难?啃完阿里老哥这套Java架构速成笔记,我都能拿30K
最近有不少小伙伴在后台留言,说 Java 的面试越来越难了,尤其是技术面,考察得越来越细,越来越底层。
Java面试很难?啃完阿里老哥这套Java架构速成笔记,我都能拿30K