java特种兵读书笔记(2-1)——计算机原理CPU

本文介绍了计算机缓存的就近原则,探讨了多核CPU的缓存一致性问题。同时,讲解了Java中的堆栈管理,强调了栈空间的自动回收。此外,还讨论了IO与上下文切换、JVM多线程模型以及并发控制中的锁机制,特别是synchronized关键字的应用。最后,提到了线程数设置策略,根据CPU密集型和IO密集型任务的特点,给出了合理的线程池大小建议。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

就近原则


计算机的缓存结构设计——就近原则,举例处理器越近,操作延迟就越小。

平时设计系统的缓存也是,内存缓存最快,redis缓存有网络传输,距离更远,延迟会更大。

多核的cache之间会存在缓存一致性的问题。

堆和栈


对C/C++来说,如果不是通过malloc,new或者realloc来分申请空间的话,那么作用域结束时空间自动释放。这部分回收操作时通过OS来控制的。

java的栈空间类似,是由java和OS来一起管理的一块区域。

java程序的对象实例的空间大部分存储在java的heap中。

当程序的局部变量使用基本类型时,它直接在栈上申请空间,而使用引用来引用对象时,这些引用的空间也位于栈上。

本地变量


在编译期,java就可以决定方法中本地变量的个数,因此在方法调用的时候,可以直接分配一个本地变量区域。该空间基于slot来分配,每给slot占用32bit,即使boolean也会占用这么宽,double和long会占用2个slot。

slot可以复用,在方法体内,如果在某个if或者for循环中声明的局部变量,在退出该区域后,slot会被释放给在它后面声明的变量。

IO与上下文切换


线程已经执行了一部分内容,需要记录下其内容和状态,中途由于调度算法(分时间片,调用让步或者休眠)或者阻塞等原因,导致还未完全结束的线程离开CPU的调度,此时需要保留线程,以便再次执行的时候找到上一次执行的位置。

比如一次普通的网络API调用,一次数据库访问,一次磁盘请求(写一条没有缓冲区的日志),此时线程会陷入blocking状态,以至于发生所谓的上下文切换

JVM与多线程


进程独立向OS申请资源,线程共享进程的资源。

CPU调度的最基本单位是线程,java是基于多线程模型的,因为资源共享,所以在java中可以让一个静态的数据变大,将一个JVM进程挂掉,而这种情况在多进程中一般碰不到。

log4j异步写日志


日志写操作只是将日志写入到一个消息队列中,由单独的线程来完成日志写操作,这样降低了许多IO竞争,将许多随机写操作编程顺序写操作,从而提升平均性能。

如果用阻塞的模式,就会阻塞当前执行业务操作的线程。

并发


web程序的并发首先由web容器已经处理好了本地变量分配,几乎不用关注并发。但当程序并发访问共享的内存区域的时候,同样要考虑并发。并发访问某些框架也一样,比如并发访问连接池,连接池如何设计!

Synchronized关键字包含区域代表临界区,临界区的范围取决于“加锁的对象”已经“跨越的代码段”。如果过个线程同时尝试进入临界区,那么即使有多个CPU,也只允许一个线程进入该区域,其他的进入等待队列,进入blocking状态

征用


征用类似于过独木桥,同一时刻只允许一个人在上面。如果桥很长,程序就会被串行化,无论有多少个CPU,性能也是单核的性能,甚至更差,因为征用也有开销。

征用带来更多的是同步开销,它要求CPU处理中不允许有其他的线程进入临界区,同时需要等待的线程放入等待队列,等当前线程退出临界区,还要唤醒被阻塞的线程。

即一个线程进入->其余线程进队列阻塞->第一个线程结束->唤醒等待队列中的线程。

桥可以变的短一些,分成一截截,每一截都可以有一个人在上面,这就有点像异步。这里涉及锁粒度的划分,在此基础上还会产生很多乐观机制。

多线程,上下文切换以及线程数设置


当多个线程同时请求一个CPU处理时,CPU会将这些任务队列化,基于时间片,优先级以及任务大小等因素进行分别调度。这样就会产生上下文切换。

对于CPU密集型,理想线程数往往是CPU±1,目的是让CPU转起来,而不只是看到CPU的使用量升高。如果系统扛不住了,增大线程池中的线程数,有时往往效果更差,因为在调度时会有大量的上下文切换。表面上CPU使用在升高,但更多的是在做一些上下文切换时的加载工作和保存现场工作。

CPU密集型具体举例:如果是8核,即8个CPU,如果设置有50个线程,那么来一个操作,占一个线程,来了50个操作,有8个在进行,42个在等CPU,这42个线程就需要阻塞并等待CPU分配调度,成本太高,如果设置8个线程,那么有8个线程在占8个CPU,其他操作等待拿线程,如果拿到了线程说明也抢到了CPU,调度成本小。

对于IO密集型系统,线程数可以设置大一些。因为系统大部分时间都在做IO交互,这个时间线程不会占用CPU来进行处理(但通常会记录下这个线程在等待IO,以便IO数据返回是,可以将其唤醒,被CPU再次调度),也就是说在这个时间范围内,可以由其他的线程来使用CPU,因此可以多配置一些线程。

IO密集型具体值举例:如果一段关键程序(被频繁访问的程序)花费120ms,其中IO花费100ms,计算(普通常规操作)花费20ms,那么线程数可以设置为6,因为100ms等待时CPU是可以被其他线程访问的,即可以用来做计算,100ms/20ms=5,加上当前线程即6。

计算机零部件中最慢的就是IO操作,只要发生IO操作,占用时间一般都较长。IO操作包括:网络IO,磁盘IO,键盘IO,屏幕输出IO。

锁与线程数设置


即临界区范围,如果锁内有IO,或者大量循环以及递归处理,那么必须操作完结才能有下一个线程进入处理。

首先看这个对象是不是Class或者静态对象,如果是可以认为是一个JVM进程全局锁,无论配置多少线程都是一样的,如果配置多了,其余线程还得等待与分配,效率可能更差。

JVM调节


即使CPU很快,如果JVM跟不上,或者不断GC或者其他因素,那么系统依然会很慢。

我们不愿意让程序在磁盘(Swap)上跑,除非SSD,宁愿宕机也不愿意程序很慢。因为程序在磁盘上跑会不断发生IO操作,我们认为程序很正常,但是运行却很慢,又找不到原因。

可以根据JVM中Young GC的平均时间间隔,以及系统QPS,来估算每个请求占用的内存大小。(通常一个请求分配的空间都在Eden区,Eden区满了就发生Young GC)。加入10s钟Young GC,QPS是n,Eden区大小是E,那么每个请求的大小就是E/(10*n)。





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值