java -池化技术

 java性能优化,通常要考虑GC, 线程上下文切换,网络IO操作的影响;池化技术可在一定场景下很好的规避这些问题,如对象(内存)池,线程池,连接池等; 本文讲几个典型案例; 

一. 规避GC--对象池

  apache common-pool对象池,对象复用,完整的状态管理;

java -池化技术_第1张图片

 二. 规避线程上下文切换损失---线程池

 1 线程池主要类型:

newCachedThreadPool如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程;
 
newFixedThreadPool创建一个指定工作线程数量的线程池,不能处理任务暂时放如队列;
 
newSingleThreadExecutor创建一个单线程,发生异常,新建一个来替代,保证所有任务按照指定顺序(FIFO, LIFO,优先级)执行
 
newScheduleThreadPool创建一个定长的线程池,而且支持定时的以及周期性的任务执行

 2 线程池使用注意事项

线程安全
选择合适的构造参数,如区分是计算cpu密集型还是io密集型,前者选择核心线程数可根据cpu cores;
不要将异常直接抛给线程;
合理关闭,防止拖垮操作系统资源

三. 规避io网络连接 ---链接池 

1 druid数据源 :

优点--维持到指定数据库的多个可用连接池;包含调用统计、慢查询统计、断路重连;
   曾遇到问题:拿不到链接;
   排查原因:慢查询太多,maxActive设置太低;解决方法,临时增加maxActive值,后续解决掉慢查询;

2  httpClient:

  优点:维持到指定路由的http连接池,省去了tcp的3次握手和4次挥手的时间,极大降低请求响应的时间
   适合场景:服务之间的互相调用,且调用比较频繁;
   注意项:  不要直接关闭http链接,而是交给连接池来处理;

3 lettuce

     每个节点保持一个链接,不存在线程安全问题,基于netty维持链接池;多路复用,事件驱动;

四. 规避native内存申请和GC---本地内存池

1 . JVM直接内存的特征

a.申请:相对于堆内存,申请效率较低
b.GC:
  JVM中的直接内存,通常在老年区满了触发Full GC时,才会触发回收;
  JVM中的直接内存,存在堆内存中其实就是DirectByteBuffer类,和实际内存就是引用关系;DirectByteBuffer熬过了几次young gc之后,会进入老年代。当老年代满了之后,会触发Full GC。因为本身很小,很难占满老年代,因此基本不会触发Full GC,带来的后果是大量堆外内存一直占着不放,无法进行内存回收;
   每次申请直接内存,都先看看是否超限 —— 直接内存的限额默认(可用 -XX:MaxDirectMemorySize 重新设定)。
如果超过限额,就会主动执行System.gc(),这样会带来一个影响,系统会中断100ms。如果没有成功回收直接内存,并且还是超过直接内存的限额,就会抛出OOM——内存溢出。
c.好处: 减少内次copy

2. JVM直接内容使用优化方案---直接内存池

3. netty内存池

   a.内存分级从上到下主要分为:Arena,ChunkList,Chunk,Page,SubPage五级 

java -池化技术_第2张图片

 

b.内存池内存分配入口是PoolByteBufAllocator类,该类最终将内存分配委托给PoolArena进行;为了减少高并发下多线程内存分配碰撞带来的性能影响,PoolByteBufAllocator维护着一个PoolArena数组,线程通过轮询获取其中一个进行内存分配,进而实现锁分离;

c.内存分配的基本单元是PoolChunk,从PoolArena中分配获取一个PoolChunk,一个PoolChunk包含多个Page内存页,通过完全二叉树维护多个内存页用于内存分配--请参照slab分配,Buddy(伙伴)分配;
 

4. netty可回收对象池

Netty自己实现了一套轻量级的对象池。在Netty中,通常会有多个IO线程独立工作,基于NioEventLoop的实现,每个IO线程轮询单独的Selector实例来检索IO事件,并在IO来临时开始处理。最常见的IO操作就是读写,具体到NIO就是从内核缓冲区拷贝数据到用户缓冲区或者从用户缓冲区拷贝数据到内核缓冲区。这里会涉及到大量的创建和回收Buffer,Netty对Buffer进行了池化从而降低系统开销。

a.借用:首先判断池中是否存在对象,如果由则优先从本地线程stack中获取Object,如果stack为空时,再将其他线程queue集合的所有对象根据一定的规则转移到本地线程stack中(1/7规则),然后再从stack获取Object并返回.如果池中不存在对象,创建对象并返回。

b.回收:如果当前回收的线程是原始线程(创建对象的线程),如果超过Stack的容量(默认4*1024)或者1/7规则 就drop(由jvm回收释放)
 

你可能感兴趣的:(java,java)