应用服务器性能优化 (代码优化)
1. 多线程
多用户并发访问是网站的基本需求,大型网站的并发用户数会达到数万,单台服务器的并发用户也会达到数百。CGI编程时代,没用户请求都会创建一个独立的系统进程去处理。由于线程比进程更轻量,更少占有系统资源,切换代价更小,所以目前主要的Web应用服务器都采用多线程的方式响应并发数用户请求,因此网站开发天然就是多线程编程。
从资源利用的角度看。使用多线程的原因有两个:IO阻塞与多CPU。当线程进行IO处理的时候,会被阻塞释放CPU以等待IO操作完成,由于IO操作(不管是磁盘IO还是网络IO)通常都需要较长时间,这是CPU可以调度其他线程进行处理。
网站的应用程序一般都被Web服务器容器管理,用户请求的多线程也通常被Web服务器管理,但不管Web容器管理线程还是应用程序自己创建的线程,一台服务器启动多少线程合适呢?
启动线程数 = [任务执行时间/(任务执行时间=IO等待时间)] x CPU内核数
最佳启动线程数和CPU内核数量成正比,和IO阻塞时间成反比。如果任务都是CPU计算型任务,那么线程最多不超过CPU内核数。
将对象设计为无状态对象:所谓无状态对象是指对象本身不存储状态信息,这样多线程并发访问的时候就不会出现不一致,Java Web 开发中查那个用到的Servlet对象就是无状态对象,可以被应用服务器多线程并发调用处理用户请求。
使用局部对象:即在方法内部创建对象,这些对象会被每个进入该方法的线程创建,除非程序有意识的将这些对象传递给其他线程,否则不会出现对象被多线程并发访问的情形。
并发访问资源时使用锁:即多线程访问资源时,通过锁的方式使用多线程并发操作转化为顺序操作,避免资源被并发修改。
2. 资源复用
系统运行时,尽量减少那些开销很大的系统资源的创建和销毁,比如数据库连接、网络通信链接、线程、复杂对象等。从编程的角度,资源复用主要有两种模式:单例(Singleton)和对象池(Object Pool)。
3. 数据结构
程序就是数据结构+算法,数据结构对于编程的重要性不言而喻。
前面缓存描述过Hash表的基本原理,Hash表的读写性能很大程度上依赖HashCode的随机性,即HashCode越随机散列,Hash表冲突越少,读写性能也就越高,目前比较好的字符串Hash散列算法有Time33算法,即对字符串逐字符迭代乘以33,求的Hash值,算法原型为:
hash(i) = hash(i-1) * 33 + str[i]
另一个则是对字符串取信息指纹,再对信息指纹求HashCode。
4. 垃圾回收
以JVM为例,其内存主要划分为堆(heap)和堆栈(stack)。