java性能优化笔记(二)设计优化

版权声明:本文来自kid_2412的csdn博客,欢迎转载! https://blog.csdn.net/kid_2412/article/details/52496787

设计优化手段:

  • 设计模式:使用常用设计模式改善代码,优化运行时产生的对象、对象生命周期等。
  • Value-Object:合并网络请求,减少网络请求。
  • 业务代理:对业务模块封装代理层,代理层缓存远程调用的请求数据。
  • 缓存和缓冲区:缓存热区数据,对延时比较大的功能使用缓冲技术。
  • 池技术:使用线程池、连接池、对象池等技术减少对象、连接的重新创建。
  • 并行化:充分利用cpu资源,采用多进程、多线程、协程等技术。
  • 负载均衡:分发请求,减小单机压力。
  • 时间和空间:适当情况下可以以内存或硬盘空间换取程序执行效率,同样也可以降低程序执行效率来节省内存或硬盘空间。

性能优化可采用的设计模式:

  • 单例模式:保证应用中只存在一份对象实例,减少对象产生,节省内存空间,gc回收压力减轻。
  • 享元模式:多业务模块共享对象实例,减少对象产生,节省内存空间,gc回收压力减轻。
  • 工厂模式:采用工厂生产对象实例,保证对象实例在适当的情况下创建,间接性优化对象生命周期。
  • 代理模式:封装代理模块,可对被代理对象进行安全检查、缓存、对象实延迟实例化、延迟建立网络连接或磁盘I/O操作等。
  • 装饰者模式:在对象构建时包装对象,最常见的是Java的流操作BufferedInputStream包装FileInputStream进行缓冲操作。
  • 观察者模式:在业务中,若A依赖B的处理状态时,可用观察者模式替代A对B的状态轮询,B会以回调的方式通知A。

Value-Object:

常见的Value-Object场景:循环中查询数据库,可改成集中一次性查询(如:把id用逗号分隔,使用in或exists去查询),然后通过Map(id作为key关联)以Value-Object的形式对应取出查询结果。

业务代理模式:

在RPC调用中我们可以使用代理对模块拆分,将代理提供的接口暴露给客户端调用,代理中可对客户端的重复调用进行缓存来减少网络请求,或将多个RPC调用进行合并,也可以将PRC直接的调用依赖拆分,以事件驱动方式处理返回结果。同样也可以在代理中做适当的安全性校验。

缓存和缓冲区:

  • 缓存:最常用的缓存技术有
    • 应用内缓存:jvm或容器内的缓存,通常可以用application实现,当然也有专业的缓存框架,如oscache、ehcache等,当然也可以使用HashMap自己实现。
    • 全局缓存:全局性的缓存服务,如redis、memchached等,本类型的缓存基本都是独立的缓存服务器,应用通过api访问缓存。由于单独的缓存服务,所以不受应用内存或jvm内存大小的限制,也可以方便动弹的横向扩容。
  • 缓冲区:常见的缓冲区技术如java的文件流读写缓冲BufferedInputStream或BufferedOutpuStream,将字节流缓冲到内存中逐渐写入硬盘。

池技术:

  • 池分类:线程池、对象池、连接池。
  • 池实现:

    • 线程池:java的Executors提供四种线程池:

      • newCachedThreadPool:创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。线程池为无限大,当执行第二个任务时第一个任务已经完成,会复用执行第一个任务的线程,而不用每次新建线程。
      • newFixedThreadPool:创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。定长线程池的大小最好根据系统资源进行设置。如Runtime.getRuntime().availableProcessors()。
      • newScheduledThreadPool:创建一个定长线程池,支持定时及周期性任务执行。
      • newSingleThreadExecutor:创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
    • 对象池:一般可自己使用Vector实现,或使用apache的common-pools实现。也可以通过全局缓存实现对象池。

    • 连接池:数据库的连接池主要有c3p0、dbcp等。

并行化:

  • 多进程:通过操作系统提供的fork和exec系统调用产生子进程,主进程与子进程之间采用共享内存、消息队列、管道、信号、unix域内套接字等方式通信。多进程虽然占用内存比较大(子进程会复制父进程的堆栈段、数据空间),但处理速度比较快。注意java中没有多进程概念。
  • 多线程:充分利用cpu时间片,将进程划分更细小的线程(子线程只得到了父线程的代码段),由于只得到了代码段,所以要比多进程节省空间,但由于得到的少自然在线程之间要共享数据。由此锁是必不可少的,同时线程间通信还有:信号量、信号、条件。
  • 协程:将线程再进行更细小的切分,协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈(状态寄存器)。

负载均衡:

  • 常用负载技术:
    • 硬负载:F5、思科、梭子鱼、AppDirector系列。
    • 软负载:LVS(4层负载)、nginx(7层负载)、apache(7层负载)、HAProxy(4-7层负载)等。这里主要说明软负载技术。
  • 负载策略:基于权重(weight)、轮询(round robin)、hash ip算法、主从(master-slave)。
  • 软负载方案:nginx+tomcat、apache+tomcat,需要注意session问题:
    • 黏性session:session通过hash算法绑定在一台tomcat上。一台宕机会丢失session,出现单机故障。
    • session复制:tomcat之间广播同步session,并发大以后容易产生广播风暴。
    • 缓存共享session:可使用nginx的memcached session缓存模块或Terracotta服务。也可以自己通过token/jsessionid+缓存/数据库实现session管理。

时间和空间:

  • 时间换空间:最常见的例子是在冒泡排序中不适用第三个临时变量交换前后两个变量,请参考如下代码:

    • 使用第三个临时变量:
      a=tmp;
      a=b;
      b=tmp;

    • 不使用第三个变量:
      a=a+b;
      b=a-b;
      a=a-b;

  • 空间换时间:最典型的例子是通过缓存来加快热数据的读取速度,或使用对象池减小对象创建和销毁的开销。

猜你喜欢

转载自blog.csdn.net/kid_2412/article/details/52496787
今日推荐