抱歉,您的浏览器无法访问本站

本页面需要浏览器支持(启用)JavaScript


了解详情 >

Java特种兵 - Java程序员需要知道的计算机原理,Reading Notes

1. 计算机原理

计算机总体体系结构的变化,一直不是特别大,基础原理将引导我们从整体上认识计算机本身。

2. CPU

每个进程 or 线程 发出请求, 最后会由 CPU 来分配时间片处理,处理时 操作数 传递给 CPU, CPU 计算将其回写到本地变量。这个本地变量通常会存在程序所谓的 栈 中,多次对其操作,它可能会被 Cache 到 CPU 的缓存之中。CPU 有 寄存器,一级缓存,二级缓存 … , 其实设计这些组件就是为了那四个字 就近原则

2.1 Cpu 联系 Java

在 编译阶段,Java 就可以决定方法的 LocalVariable 的个数,在方法调用的时候,就可直接分配一个 LocalVariable 区域,这个空间是基于 slot 来分配的,每个 slot 占用 32 bit,boolean 占用 1 slot,long and double 占 2 个 slot。

LocalVariableTable :

Start Length Slot Name Signature
0 9 0 args Ljava/lang/String;
2 7 1 a I
  • Start : 代表LocalVariable在虚指令作用域的起始位置
  • Length : 代表LocalVariable在虚指令作用域的长度(如第1个本地变量args是9条指令的作用域)

2.2 多核

为了多个计算中心同时做事情,增加效率。

考虑需要和解决的问题

一个指令来了,哪个Cpu来处理? 同一份数据被多个 Cpu 处理,如何协调并让其他Cpu都知道。
当发起一个计算请求,例如一个中断,这么多 Cpu 会干什么?

中断 : 指当出现需要时,CPU暂时停止当前程序的执行转而执行处理新情况的程序和执行过程。

Cpu 的计算速度非常快,OS不希望它等待或者停止,所以在出现 I/O 等待 (网络I/O 和 磁盘I/O), 它中途基本不参与,而以事件注册的方式来实现回调,对于某些执行时间长的task,Cpu会分配一些时间片执行其他的task.

当 Cpu 不断去切换 task 处理时,这就会涉及到 上下文切换.

2.3 Cache line

办事就近原则,可以一次办多件事情 或 一件事情的多个步骤可以一次办完。

Cache line 就是 将 连续的一段内存区域 进行 Cache,不是每次就 Cache 一个内存单元,而是一系列内存单元。计算机中,通常以连续 64bit 进行Cache。

感悟 : Cache Line 就是 一次拿一批信息去处理。 Cache line 的目的是为了 快速访问。

2.4 缓存一致性协议

当有 来自于 内存 中的同一份数据 Cache 在多个 CPU 中,且要求这些数据的读写一致时,多个 CPU 之间就需要遵循缓存共享的一致性原则。

相当于大家都来修改一份设计报告,大家都拷贝了一份,回家修改,每个人修改要及时让其他人都知道。有点像版本控制

内存单元的状态
Modified
Exclusive
Shared
Invalid

多个 CPU 通过总线相互连接,每个 CPU 的cache处理器 要响应本身所对应的CPU读写操作外,还需要监听总线上其他CPU操作,通过监听对自己的Cache做处理,形成虚共享,这个协议叫做 MESI 协议。

一个数据修改了,它需要告诉其他 CPU 这份数据被修改了,现在Intel通过 QPI 来完成。不同CPU之间交互需要时间 20~40 ns 级别。

1
2
3
4
5
6
7
class VolatileInteger {
volatile int number;
}
VolatileInteger[] values = new VolatileInteger[10];
for (int i = 0; i < 10; i++) {
values[i] = new VolatileInteger();
}

这段代码的例子,很可能使的每个CPU可能只修改到某个元素,但会有大量 QPI 存在。

QPI : Quick Path Interconnect

2.5 Context switch

线程已经执行了一部分内容,需要记录下它的内容和状态,中途由于调度算法。

CPU 调度 的最基本单位是线程,Java也是基于多线程模式。由于多线程模型中多个线程共享进程的资源,所以Java程序,如某一个线程占用资源过大时,就可能导致整个JVM进程挂掉。(影响都是相对的)

在实际运行中会有代码段和数据段,内容切换时要保存这些运行中的上下文信息,再使用的时候,再加载回来。

日志写操作 (如 : log4j) 都采用 异步 模式 实现,而程序通常不直接参与这个过程。 实现方式 (日志写操作只是将日志写入一个消息队列中,由单独的线程来完成写操作)

2.6 并发与争用

只要是服务器端程序,迟早会遇到并发。当 并发 时,就会存在对各种资源的争用,包括对各个部件(如CPU)的争用。

Web 程序也会经常遇到并发问题的,编写者,没有遇到是因为 Web 容器帮助处理好了线程的本地变量分配,我们几乎不用关注并发。

2.6.1 临界区

当程序出现 “加锁” 时 (如 Java的 synchronized) 说明这一块是临界区,只允许一个线程访问,其他线程来回进行等待队列。
争用带来的是同步的开销,它会发出许多指令要求所有CPU处理中不允许其他线程进入临界区,且需要将等待线程放入队列阻塞。
争用 CPU 的访问也不仅仅体现在锁上面,CPU本身数量也有限。 单个CPU会对任务进行 基于 时间片、优先级、任务大小分别调度。

2.6.2 线程池数

在理想的 CPU 密集型系统,线程数是 CPU数+1 / CPU-1

  • 系统CPU密集度

一般系统分为 计算密集型 和 I/O 密集型。

系统中关键程序访问总共花费 120ms, I/O操作 占用 100ms,100ms时间内 CPU是可以被其他线程访问的。此时,这个程序在单核系统中的线程数理论上可以设置为 6, 在多核系统就是乘以CPU个数 左右这个数字。

一般这个线程数的决定是通过测试的。

2.6.3 锁

就是临界区的范围,有粒度。如果在 锁 内部发送 I/O, 大循环、递归等,那么就必须等待这些操作处理完成后才能有下一个线程进入处理。如果这段程序是 关键程序, 当系统真正并发的时候,很多线程都会阻塞在这里。这时要计算线程数,要看锁对象 是不是静态对象或Class, (如果是,则是一个JVM全局锁),无论配置多少线程效果都一样。锁是全局的,无论多少个CPU也是无济于事的。

结论 : 锁尽量不要设置为 全局锁,能用粒度控制,尽量粒度控制

2.6.4 JVM自身调节

不论 CPU 跑多快,如 JVM hold不住节奏,不断做GC,那么如何配置线程池,系统性能还是上不来。

可以根据 JVM 运行日志中,平均做 Young GC 的时间间隔 (通过 Young GC 与 运行时长对比),以及系统的QPS,来估算每个请求大致占用的内存大小,有时不准,但具有参考价值。

如何计算

Eden 空间的大小我们是知道的。通常一个请求分配空间都在 Eden 区域,Eden区域满发生Young GC。Young GC 时间间隔就是Eden满的时间间隔,例如 3s, 进一步通过 QPS*3 得到多少个请求可以填充满 Eden 区域。这可初步估算每个请求占用的内存空间。

3. 内存

基本所有的程序猿与程序媛都知道,它是跑程序的地方。

磁盘存储 与 CPU 之间的桥梁。拥有比 CPU缓存 大几百倍、上千倍的空间。CPU三级缓存也就 几十M。

磁盘 到达 CPU需要经过 (主板-南桥、北桥) 才能到达CPU。很慢。

内存的容量都是 GB 单位,大量程序运行都依赖内存,又 OS 来管理和调度。

地址位数、逻辑地址、虚拟地址、物理地址、线性地址、内核区域等,很多人看到这,直接疯了,但是你需要淡定。

3.1 虚拟地址

所有的程序中使用的地址都是虚拟地址 (在段式管理中也叫逻辑地址),这些地址在不同的进程之间是可以重复的。

程序为什么要使用 虚拟地址?

C语言来说,编译后的指令中,许多调用的地址在编译阶段就得确定下来,许多方法入口和变量位置在编译时确定了虚拟地址,真正运行时要由OS来分配实际的地址给程序。
使用虚拟地址后,地址是可以被复用的,程序不关心与其他进程是否会使用同一地址,OS会分配确保。

3.2 分段机制

也就是为进程分配的一段内存区 (连续的区域),它的 起始位置 + 逻辑地址 = 线性地址 (就是物理地址)

本进程访问其他进程内存,内存不能read 错误。

3.3 分页机制

分页机制 可支撑较大的内存,物理上大多将其划分为 4KB/页

3.4 Java Heap

Java语言,主要看 Heap 区域,系统参数设置为 -Xms, -Xmx 时,JVM 通常是申请一个连续的虚拟地址。OS预先分配的物理内存空间是 -Xms 的大小, -Xmx 许多空间真正使用的时候才会分配。

32 bit 系统中,1.5GB 的 Heap 区域是比较合适的。64bit 空间不会受到限制 (JVM也必须换成64bit模式)

4. Disk

disk 一直在拖着计算机的后腿, SSD好一些.

5. Cache

CPU 有 cache,系统架构有 cache,存储上有cache,分布式上有 cache,数据库上有cache,ORM框架上有cache …

Cache 就是 靠近原则

6. 网络与数据库

6.1 Java 基本 IO

只要是内存程序的通信,都可以理解为 Input / Output