Java程序员需要掌握的计算机底层知识(一):CPU基本组成、指令乱序执行、合并写技术、非同一访问内存 NUMA
一些书籍
读书的原则:不求甚解,观其大略
你如果进到庐山里头,二话不说,蹲下头来,弯下腰,就对着某棵树某棵小草猛研究而不是说先把庐山的整体脉络跟那研究清楚了,那么你的学习方法肯定效率巨低而且特别痛苦,最重要的还是慢慢地还打击你的积极性,说我的学习怎么那么不happy啊,怎么那么特没劲那,因为你的学习方法错了,大体读明白,先拿来用,用着用着,很多道理你就明白了
▪《编码:隐匿在计算机软硬件背后的语言》
▪《深入理解计算机系统》
▪语言:C JAVA K&R《C程序设计语言》《C Primer Plus》
▪ 数据结构与算法: – 毕生的学习 leetCode
–《Java数据结构与算法》《算法》
–《算法导论》《计算机程序设计艺术》//难
▪操作系统:Linux内核源码解析 Linux内核设计与实现 30天自制操作系统
▪网络:机工《TCP/IP详解》卷一 翻译一般
▪编译原理:机工 龙书 《编译原理》 《编程语言实现模式》马语
▪数据库:SQLite源码 Derby - JDK自带数据库
硬件基础知识
CPU的制作过程
Intel cpu的制作过程
https://haokan.baidu.com/v?vid=11928468945249380709&pd=bjh&fr=bjhauthor&type=video
CPU是如何制作的(文字描述)
https://www.sohu.com/a/255397866_468626
汇编语言(机器语言)的执行过程
汇编语言的本质:机器语言的助记符 其实它就是机器语言
计算机通电 -> CPU读取内存中程序(电信号输入)
->时钟发生器不断震荡通断电 ->推动CPU内部一步一步执行
(执行多少步取决于指令需要的时钟周期)
->计算完成->写回(电信号)->写给显卡输出(sout,或者图形)
量子计算机
量子比特,同时表示1 0
CPU的基本组成
PC -> Program Counter 程序计数器 (记录当前指令地址)
Registers -> 暂时存储CPU计算需要用到的数据
ALU -> Arithmetic & Logic Unit 运算单元
CU -> Control Unit 控制单元
MMU -> Memory Management Unit 内存管理单元
Java相关硬件知识
cache 缓存
一致性协议:https://www.cnblogs.com/z00377750/p/9180644.html
缓存行:
缓存行越大,局部性空间效率越高,但读取时间慢
缓存行越小,局部性空间效率越低,但读取时间快
取一个折中值,目前多用:64字节
证明缓存行的存在:
T01_CacheLinePadding.java
package com.mashibing.juc.c_028_FalseSharing;import java.util.Random;public class T01_CacheLinePadding {private static class T {public volatile long x = 0L;}public static T[] arr = new T[2];static {arr[0] = new T();arr[1] = new T();}public static void main(String[] args) throws Exception {Thread t1 = new Thread(()->{for (long i = 0; i < 1000_0000L; i++) {arr[0].x = i;}});Thread t2 = new Thread(()->{for (long i = 0; i < 1000_0000L; i++) {arr[1].x = i;}});final long start = System.nanoTime();t1.start();t2.start();t1.join();t2.join();System.out.println((System.nanoTime() - start)/100_0000);}
}
// 输出:1163
T02_CacheLinePadding.java
package com.mashibing.juc.c_028_FalseSharing;public class T02_CacheLinePadding {private static class Padding {public volatile long p1, p2, p3, p4, p5, p6, p7;}private static class T extends Padding {public volatile long x = 0L;}public static T[] arr = new T[2];static {arr[0] = new T();arr[1] = new T();}public static void main(String[] args) throws Exception {Thread t1 = new Thread(()->{for (long i = 0; i < 1000_0000L; i++) {arr[0].x = i;}});Thread t2 = new Thread(()->{for (long i = 0; i < 1000_0000L; i++) {arr[1].x = i;}});final long start = System.nanoTime();t1.start();t2.start();t1.join();t2.join();System.out.println((System.nanoTime() - start)/100_0000);}
}
// 输出:548
以上两个小例子证明了缓存行的存在。
缓存行对齐:对于有些特别敏感的数字,会存在线程高竞争的访问,为了保证不发生伪共享,可以使用缓存航对齐的编程方式
JDK7中,很多采用long padding提高效率
JDK8,加入了@Contended
注解(实验)需要加上:JVM -XX:-RestrictContended
CPU 的乱序执行
乱序执行的意思,实际上是在同时执行。本质上是在提高效率。
一个小例子,证明乱序的存在:
package com.mashibing.jvm.c3_jmm;public class T04_Disorder {private static int x = 0, y = 0;private static int a = 0, b =0;public static void main(String[] args) throws InterruptedException {int i = 0;for(;;) {i++;x = 0; y = 0;a = 0; b = 0;Thread one = new Thread(new Runnable() {public void run() {//由于线程one先启动,下面这句话让它等一等线程two. 读着可根据自己电脑的实际性能适当调整等待时间.//shortWait(100000);a = 1;x = b;}});Thread other = new Thread(new Runnable() {public void run() {b = 1;y = a;}});one.start();other.start();one.join();other.join();String result = "第" + i + "次 (" + x + "," + y + ")";if(x == 0 && y == 0) {System.err.println(result);break;} else {//System.out.println(result);}}}public static void shortWait(long interval){long start = System.nanoTime();long end;do{end = System.nanoTime();}while(start + interval >= end);}
}
执行结果:出现了 0,0
,证明了乱序的存在
上面这个小例子来源于外国人写的一个博客:
https://preshing.com/20120515/memory-reordering-caught-in-the-act/
乱序可能存在的问题:
如何禁止乱序
CPU层面:
(1)使用原语
Intel CPU 的实现方式是,使用原语(汇编指令mfence lfence sfence
或者锁总线(都属于硬件实现)
(2)Intel lock
汇编指令
JVM层面
因为不是所有的CPU都有汇编指令的实现,因此JVM在实现的时候使用的是lock
指令。
JVM层级:8个hanppens-before原则、4个内存屏障 (LL LS SL SS)
名词as-if-serial
的含义: 不管硬件什么顺序,单线程执行的结果不变,看上去就像是顺序执行的一样。
拓展:很多请求打进来,如果想要顺序执行,可以使用SingleThreadPool。为了避免内存溢出,有界队列满了可以采取拒绝策略 。
JVM在写操作和读操作前后都加了屏障。而屏障的实现,就是使用lock
指令。
合并写技术 Write Combining
寄存器和L1缓存之间还有一个buffer,空间特别小。另外还有一个WC(Write Combining)Buffer,一般是4个字节
由于ALU速度太快,所以在写入L1的同时,写入一个WC Buffer,满了之后,写满4个字节之后,才会一次性刷到缓存L2里。可以用程序证明。
证明 WC 的存在:
package com.mashibing.juc.c_029_WriteCombining;public final class WriteCombining {private static final int ITERATIONS = Integer.MAX_VALUE;private static final int ITEMS = 1 << 24;private static final int MASK = ITEMS - 1;private static final byte[] arrayA = new byte[ITEMS];private static final byte[] arrayB = new byte[ITEMS];private static final byte[] arrayC = new byte[ITEMS];private static final byte[] arrayD = new byte[ITEMS];private static final byte[] arrayE = new byte[ITEMS];private static final byte[] arrayF = new byte[ITEMS];public static void main(final String[] args) {for (int i = 1; i <= 3; i++) {System.out.println(i + " SingleLoop duration (ns) = " + runCaseOne());System.out.println(i + " SplitLoop duration (ns) = " + runCaseTwo());}}public static long runCaseOne() {long start = System.nanoTime();int i = ITERATIONS;while (--i != 0) {int slot = i & MASK;byte b = (byte) i;arrayA[slot] = b;arrayB[slot] = b;arrayC[slot] = b;arrayD[slot] = b;arrayE[slot] = b;arrayF[slot] = b;}return System.nanoTime() - start;}public static long runCaseTwo() { // 一次正好写满一个四字节的 Buffer,比上面的循环效率更高long start = System.nanoTime();int i = ITERATIONS;while (--i != 0) {int slot = i & MASK;byte b = (byte) i;arrayA[slot] = b;arrayB[slot] = b;arrayC[slot] = b;}i = ITERATIONS;while (--i != 0) {int slot = i & MASK;byte b = (byte) i;arrayD[slot] = b;arrayE[slot] = b;arrayF[slot] = b;}return System.nanoTime() - start;}
}
非同一访问内存 NUMA
UMA:同一内存访问。多个CPU通过一条总线,访问同一个内存。
现在很多服务器的架构是使用NUMA的,因为UMA不以拓展:随着CPU的数量增多,许多时间被浪费在CPU争抢内存资源上。
NUMA:非同一访问内存。每个CPU有自己专属的内存,CPU对于自己插槽上的内存访问是有优先级的。
ZGC 可以做到 NUMA aware,如果探测到计算机实现了NUMA的话,分配内存会优先分配该线程所在CPU的最近内存。
Java程序员需要掌握的计算机底层知识(一):CPU基本组成、指令乱序执行、合并写技术、非同一访问内存 NUMA相关推荐
- 一网打尽:Java 程序员必须了解的计算机底层知识!
公众号后台回复"面试",获取精品学习资料 扫描下方海报了解专栏详情 本文来自公众号读者cxuan的投稿 我们每个程序员或许都有一个梦,那就是成为大牛,我们或许都沉浸在各种框架中,以 ...
- Java程序员需要掌握的计算机底层知识(三):进程、线程、纤程、中断
面试高频问题 问:进程和线程有什么区别? 答:进程是一个程序运行起来的状态(运行态),线程是一个进程中不同的执行路径(线程只是其中一个). 更为专业的回答:进程是操作系统用来分配资源的基本单位,线程是 ...
- Java程序员需要掌握的计算机底层知识(五):内核同步方法
内核同步机制 关于同步理论的一些基本概念 临界区(critical area): 访问或操作共享数据的代码段 简单理解:synchronized大括号中部分(原子性) 竞争条件(race condit ...
- Java程序员需要掌握的计算机底层知识(四):内存管理
内存管理 单进程DOS时代 DOS时代 - 同一时间只能有一个进程在运行(也有一些特殊算法可以支持多进程) windows9x - 多个进程装入内存存在的问题: 内存不够用 互相打扰 为了解决这两个问 ...
- Java程序员需要掌握的计算机底层知识(二):操作系统、内核、用户态与内核态、系统调用的执行过程
操作系统 启动过程 通电 -> bios uefi 工作 -> 自检 -> 到硬盘固定位置加载bootloader -> 读取可配置信息 -> CMOS CMOS 用来存 ...
- 非科班的java程序员该如何补充计算机基础知识,需要看哪些书?
java比较特殊,毕竟后端扛霸子,想要认真学好java,必须有好的计算机基础,可是为什么需要基础呢? 把这个问题弄明白,要学哪些基础知识就清楚多了. 第一个问题,10个用户使用的系统和1000000个 ...
- 简单计算机java程序_JAVA程序员需要知道的计算机底层基础10-操作系统引导程序的简单...
JAVA程序员需要知道的计算机底层基础10-操作系统引导程序的简单 JAVA程序员需要知道的计算机底层基础10-操作系统引导程序的简单制作 汇编实现引导程序 ; 文件名 boot.asm org 7c ...
- 计算机底层知识之CPU
❝ Only the disciplined in life are free. 唯自律者得自由 ❞ 大家好,我是「柒八九」. 想必能看到这篇文章的小朋友,大都是有一定编程能力的「程序媛.程序猿」.无 ...
- Java程序员年薪40W,他1年走了别人5年的路(技术提炼)
一.Java架构体系 1.高性能架构 2.开源框架解析 3.架构师筑基 4.微服务架构 5.团队协作开发 6.B2C商城项目实战 二.设计模式 三.数据结构与算法 四.BATJ企业面试总结 Java架 ...
最新文章
- iOS学习笔记(十三)——获取手机信息(UIDevice、NSBundle、NSLocale)
- 二叉树的递归和非递归遍历
- 1640. Check Array Formation Through Concatenation 能否连接形成数组
- java 常量池详解
- android 5.1 httpclient,【Android进阶学习】Http编程之HttpClient
- 手机浏览器看视频加载太慢怎么办,这5招用了提速快
- Dummy Sample
- Mediakit报告设备商的空间不足以执行此操作的纯MAC解法
- mysql数据库怎么导出到excel_mysql数据库表如何导出到excel
- mybatis 的入参和出参
- 美国大选2020推特相关数据
- 复现文件上传漏洞(靶场练习)
- 基于MATALB的多光谱影像与全色影像(高分辨率)的融合的几种方法
- Scriptable Build Pipeline - 2018.2 入门指南
- PLC梯形图编程练习
- easy php moodle,Windows 环境下配置easyphp+moodle方法及设置外部IP访问方法
- linux 执行jar包 不生成日志文件,jar包在linux本地运行成功, 但是jenkins构建失败...
- 2010年最骚最贱最有深度的100句话!!![
- Kali-linux密码在线破解
- 经济学计算机会成本,经济学思维方式之二——机会成本、经济利润
热门文章
- html搜索结果 重置,搜索结果和后退按钮/ HTML表格
- mysql 定时同步数据_MySQL数据同步之otter
- HDU4099(斐波那契数列与字典树)
- 26.PhantomData幽灵数据.rs
- Shell case esac语句
- 对现有的所能找到的DDOS代码(攻击模块)做出一次分析----自定义攻击篇
- Module.h 网狐的“com”工具箱
- 第16讲:异步爬虫的原理和解析
- 什么是 Go runtime.KeepAlive?
- 搞定系统设计 03:系统设计面试的答题框架