原文作者:杨冠标
原文地址:常见池化技术
在系统开发过程中,我们经常会用到池化技术来减少系统消耗,提升系统性能。
- 线程池通过复用线程提升性能。简单来说,池化技术就是通过复用来提升性能。
- 对象池通过复用对象来减少创建对象、垃圾回收的开销;
- 连接池(数据库连接池、Redis连接池和HTTP连接池等)通过复用TCP连接来减少创建和释放连接的时间。
线程、内存、数据库的连接对象都是资源,在程序中,当你创建一个线程或者在堆上申请一块内存的时候都涉及到很多的系统调用,也是非常消耗CPU的。如果你的程序需要很多类似的工作线程或者需要频繁地申请释放小块内存,在没有对这方面进行优化的情况下,这部分代码很可能会成为影响你整个程序性能的瓶颈。
线程池
线程池的原理很简单,类似于操作系统中的缓冲区的概念。线程池中会先启动若干数量的线程,这些线程都处于睡眠状态。当客户端有一个新的请求时,就会唤醒线程池中的某一个睡眠的线程,让它来处理客户端的这个请求,当处理完这个请求之后,线程又处于睡眠的状态。
线程池能很高地提升程序的性能。比如有一个省级数据大集中的银行网络中心,高峰期每秒的客户端请求并发数超过100,如果为每个客户端请求创建一个新的线程的话,那耗费的CPU时间和内存都是十分惊人的,如果采用一个拥有200个线程的线程池,那将会节约大量的系统资源,使得更多的CPU时间和内存用来处理实际的商业应用,而不是频繁的线程创建和销毁。
内存池
如何更好地管理应用程序内存的使用,同时提高内存使用的频率,这时值得每一个开发人员深思的问题。内存池(Memory Pool)就提供了一个比较可行的解决方案。
内存池在创建的过程中,会预先分配足够大的内存,形成一个初步的内存池。然后每次用户请求内存的时候,就会返回内存池中的一块空闲的内存,并将这块内存的标志置为已使用。当内存使用完毕释放内存的时候,也不是真正地调用free或delete的过程,而是把内存放回内存池的过程,且放回的过程要把标志置为空闲。最后,应用程序结束,就会将内存池销毁,将内存池中的每一块内存释放。
使用内存池的优点:
- 减少内存碎片的产生。这个优点可以从创建内存池的过程中看出,当我们在创建内存池的时候,分配的都是一块块比较规整的内存块,减少内存碎片的产生。
- 提高了内存的使用频率。这个可以从分配内存和释放内存的过程中看出。每次的分配和释放并不是去调用系统提供的函数或操作符去操作实际的内存,而是在复用内存池中的内存。
使用内存池的缺点:
- 缺点就是很可能会造成内存的浪费,因为要使用内存池需要在一开始分配一大块闲置的内存,而这些内存不一定全部被用到。
数据库连接池
数据库连接池的基本思想是在系统初始化的时候将数据库连接作为对象存储在内存中,当用户需要访问数据库的时候,并非建立一个新的连接,而是从连接池中取出一个已建立的空闲连接对象。在使用完毕后,用户也不是将连接关闭,而是将连接放回到连接池中,以供下一个请求访问使用。这些连接的建立、断开都由连接池自身来管理。
同时,还可以设置连接池的参数来控制连接池中的初始连接数、连接的上下限数和每个连接的最大使用次数、最大空闲时间等。当然,也可以通过连接池自身的管理机制来监视连接的数量、使用情况等。
HttpClient连接池
HttpClient我们经常用来进行HTTP服务访问。我们的项目中会有一个获取任务执行状态的功能使用HttpClient,一秒钟请求一次,经常会出现Conection Reset异常。经过分析发现,问题是出在HttpClient的每次请求都会新建一个连接,当创建连接的频率比关闭连接的频率大的时候,就会导致系统中产生大量处于TIME_CLOSED状态的连接。这个时候使用连接池复用连接就能解决这个问题。
总结
不难看出,这些池化技术的实现原理都是类似的,都是通过对连接或线程的复用,并对复用的数量、时间等进行控制,从而使系统的性能和资源消耗达到最优的状态。
实际项目中,我们可以考虑使用池化技术来解决程序性能上的瓶颈。