CPU和内存的关系
- CPU是负责运算和处理的
- 内存是交换数据的
当程序或者操作者对CPU发出指令,这些指令和数据暂存在内存里,在CPU空闲时传送给CPU,CPU处理后把结果输出到输出设备上,输出设备就是显示器,打印机等。在没有显示完之前,这些数据也保存在内存里,如果内存不足,那么系统自动从硬盘上划分一部分空间作为虚拟内存来用。但写入和读取的速度 跟物理内存差的很远很远,所以,在内存不足的时候,会感到机器反应很慢,硬盘一直在响。
512M的物理内存如果增加到2GB,你会感到电脑变得飞快。但内存512,即使你把CPU从单核换成双核,加速感觉也不明显。如果你本来就有2G内存,再增加2G,使用起来几乎没有多少性能的改变。在理论上,物理内存太大反而会减慢速度,因为它增加了寻址的时间。所以家用机器推荐使用2GB-4GB足矣。
CPU、内存、磁盘IO之间的关系
- CPU:工人,干活的,判断以及逻辑处理
- 内存:车间,工人干活的地方,车间中加工原料,当车间中没有原料了,在从仓库中取原料,对原料进行加工内存本身有一定的存储空间,对内存中的数据进行处理的速度比从硬盘取数据再处理的速度快很多
- 硬盘:仓库,原料,数据存储
CPU对数据进行判断以及逻辑处理,本身不能存储数据,这时cpu从内存取数据进行逻辑计算,如果内存没有数据,才会从硬盘读数据到内存,再对数据进行处理
就像人吃饭一样,cpu就是人,内存就是碗,硬盘就是饭锅。
当cpu进程等待,会造成内存开销的增加,内存不够用的时候会用到虚拟内存,导致虚拟内存的增加,这时磁盘IO开销就会增加,系统态sy%提升,cpu开销增加
内存里数据不够用,才用磁盘中取数据。内存中的信息会随断电而丢失,硬盘中的信息会持久化存在。
CPU过高的原因
- 计算量大,比如运算,连接查询,数据统计
- 非空闲等待,比如IO等待、资源争用(同一资源被不同线程请求,而此资源又需要保持一致性,只能前一个释放后一个再访问,这样导致的等待)
- 过多的系统调用,系统调用即调用操作系统提供的程序接口,比如Java项目中写日志,会调用系统接口进行日志写操作,这样会导致系统CPU使用率过高
- 过多的中断,中断是CPU用来响应请求的机制,比如键盘的输入,鼠标的点击等都会产生中断,中断是通知CPU有任务需要响应,CPU停下正在执行的程序来响应当前的中断
CPU过高具体影响什么?
cpu使用率过高直接导致cpu温度过高,这样会简短cpu的寿命的,而且还会影响你对其他软件的正常使用,导致其他软件速度慢。<br /> 如果CPU使用一直是在85%以上的话,会对电脑有危害的, 不会对CPU产生危害。<br />CPU的利用率是指CPU做运算时,达到自己额定频率的百分比,这个利用率高的时候,能够消耗更多的电能和产生更多的热量。<br /> 超过75度会对CPU产生危害,因为当CPU温度超过75度时,除了可能烧坏CPU之外,还可能因为电子转移过快而影响CPU寿命。
内存吃紧的原因?
多数是过多的页交换和内存泄漏
页交换:内存不够用来存储需要的数据时,操作系统会把原内存中的部分内容释放掉(移除或者存入磁盘),然后把需要的内容载入,这个过程就是页交换。
一些共性认知
- 提问:工作线程数是不是设置的越大越好?
回答:肯定不是的。
- 一来服务器CPU核数有限,同时并发的线程数是有限的,1核CPU设置10000个工作线程没有意义。
- 线程切换是有开销的,如果线程切换过于频繁,反而会使性能降低。
- 提问:调用sleep()函数的时候,线程是否一直占用CPU?
回答:不占用CPU,等待时会把CPU让出来,给其他需要CPU资源的线程使用。
不止调用sleep()函数,在进行一些阻塞调用,例如网络编程中的阻塞accept()【等待客户端连接】和阻塞recv()【等待下游回包】也不占用CPU资源。
- 提问:如果CPU是单核,设置多线程有意义么,能提高并发性能么?
回答:即使是单核,使用多线程也是有意义的。
多线程编码可以让我们的服务/代码更加清晰,有些IO线程收发包,有些Worker线程进行任务处理,有些Timeout线程进行超时检测。
如果有一个任务一直占用CPU资源在进行计算,那么此时增加线程并不能增加并发,例如这样的一个代码:
while(1){ i++; }
该代码一直不停的占用CPU资源进行计算,会使CPU占用率达到100%。
通常来说,Worker线程一般不会一直占用CPU进行计算,此时即使CPU是单核,增加Worker线程也能够提高并发,因为这个线程在休息的时候,其他的线程可以继续工作。
线程数的规划
单个任务总时间=CPU运算时间+IO等待时间,当任务A在等待IO时,CPU可以切到任务B进行CPU运算。
- 假设现有8个CPU、8个线程,每个线程占用一个CPU,同一时间段内,若8个线程都运行往前跑,相比较5/6/7个线程,8个线程的效率高。
- 但若此时有9个线程,只有8个CPU,9个线程同时运行,则此时牵扯到线程切换,而线程切换是需要消耗时间的。
- 所以随着线程数越多,效率越来越高,但到一个峰值,再增加线程数量时,就会出现问题。线程太多要来回的切换,最终可能线程切换所用时间比执行时间业务所用时间还大。
- 随着线程数越多,由于线程执行的时序的问题,程序可能会崩溃或产生二义性。
廖雪峰博客中有一章节讲到:计算密集型 vs. IO密集型,摘录如下:
我们可以把任务分为计算密集型和IO密集型。
计算密集型任务的特点是要进行大量的计算,消耗CPU资源,比如计算圆周率、对视频进行高清解码等等,全靠CPU的运算能力。这种计算密集型任务虽然也可以用多任务完成,但是任务越多,花在任务切换的时间就越多,CPU执行任务的效率就越低,所以,要最高效地利用CPU,计算密集型任务同时进行的数量应当等于CPU的核心数。 计算密集型任务由于主要消耗CPU资源,因此,代码运行效率至关重要。Python这样的脚本语言运行效率很低,完全不适合计算密集型任务。对于计算密集型任务,最好用C语言编写。IO密集型,涉及到网络、磁盘IO的任务都是IO密集型任务,这类任务的特点是CPU消耗很少,任务的大部分时间都在等待IO操作完成(因为IO的速度远远低于CPU和内存的速度)。对于IO密集型任务,任务越多,CPU效率越高,但也有一个限度。常见的大部分任务都是IO密集型任务,比如Web应用。 IO密集型任务执行期间,99%的时间都花在IO上,花在CPU上的时间很少,因此,用运行速度极快的C语言替换用Python这样运行速度极低的脚本语言,完全无法提升运行效率。对于IO密集型任务,最合适的语言就是开发效率最高(代码量最少)的语言,脚本语言是首选,C语言最差。
- CPU密集型任务(批量审核属于该类型任务)
一般配置线程数=CPU总核心数+1 (+1是为了利用等待空闲)。
要进行大量的计算,消耗CPU资源,比如计算圆周率、对视频进行高清解码等等,全靠CPU的运算能力。要最高效地利用CPU,计算密集型任务同时进行的数量应当等于CPU的核心数。 - IO密集型任务
- 一般配置线程数=CPU总核心数 x 2 +1。
这类任务的CPU消耗很少,任务的大部分时间都在等待IO操作完成(因为IO的速度远远低于CPU和内存的速度)。常见的大部分任务都是IO密集型任务,比如网络传输、数据库等调用这些Web应用。对于IO密集型任务,任务越多,CPU效率越高(但也有限度) - 最佳线程数 = (1 + 线程等待时间/线程计算时间) 目标CPU的使用率 处理器核心数
- 一般配置线程数=CPU总核心数 x 2 +1。
例如:平均每个线程计算运行时间为0.5s,而线程等待时间(非计算时间,比如IO)为1.5s,目标CPU的使用率是90%,CPU核心数为8,那么根据上面这个公式估算得到:(1 + 1.5/0.5) 90% 8 = 28.8。
IO密集型主要与线程的等待时间有关,线程发送完成请求之后会去进行等待,这个时候是不占用CPU资源的,所以可以适当地增加更多的线程。
增加线程数量,可以使用jstack命令查看一下进程的线程栈,如果发现线程池中大部分线程都处于等待获取任务,则说明线程够用,如果大部分处于运行状态,可以适当地提高线程数量。
动态线程池
最简单的办法可以将线程池的数量控制做成一个可控的参数,进行参数传递。