【HikariCP源码分析】从FastList看HikariCP为什么更快?

  从FastList看HikariCP为什么更快?

  HikariCP之所以快得益于:

  1 、优化并精简字节码

  2 、使用FastList替代ArrayList

  3 、ConcurrentBag:更好的并发集合类实现

  本篇只分析FastList。

  我们从被引用处着手,PoolEntry构造函数中初始化FastList,使用了new FastList<>(Statement.class, 16);

  PoolEntry(final Connection connection, final PoolBase pool, final boolean isReadOnly, final boolean isAutoCommit)

  {

  this.connection = connection;

  this.hikariPool = (HikariPool) pool;

  this.isReadOnly = isReadOnly;

  this.isAutoCommit = isAutoCommit;

  this.lastAccessed = currentTime();

  this.openStatements = new FastList<>(Statement.class, 16);

  }

  PoolEntry初始化FastList实例,指定了size 为16。

  我们跟入,从FastList(Class clazz, int capacity)构造函数开始。

  /**

  * Construct a FastList with a default size of 32.

  * @param clazz the Class stored in the collection

  */

  @SuppressWarnings("unchecked")

  public FastList(Class clazz)

  {

  this.elementData = (T[]) Array.newInstance(clazz, 32);

  this.clazz = clazz;

  }

  /**

  * Construct a FastList with a specified size.

  * @param clazz the Class stored in the collection

  * @param capacity the initial size of the FastList

  */

  @SuppressWarnings("unchecked")

  public FastList(Class clazz, int capacity)

  {

  this.elementData = (T[]) Array.newInstance(clazz, capacity);

  this.clazz = clazz;

  }

  FastList有连个构造函数,不指定容量时,默认size为32。

  我们必须关注类注释: Fast list without range checking.FastList没有范围检查。

  package com.zaxxer.hikari.util;

  /**

  * Fast list without range checking.

  *

  * @author Brett Wooldridge

  */

  public final class FastList implements List, RandomAccess, Serializable

  {

  private static final long serialVersionUID = -4598088075242913858L;

  private final Class clazz;

  private T[] elementData;

  private int size;

  范围检查? 我们不作言语解释。

  我们通过与ArrayList的对比核心Api进行分析。

  1)FastList#get

  /**

  * Get the element at the specified index.

  *

  * @param index the index of the element to get

  * @return the element, or ArrayIndexOutOfBounds is thrown if the index is invalid

  */

  @Override

  public T get(int index)

  {

  return elementData[index];

  }

  直接返回指定元素。如果index无效会抛出ArrayIndexOutOfBounds。

  ArrayList#get

  /**

  * Returns the element at the specified position in this list.

  *

  * @param index index of the element to return

  * @return the element at the specified position in this list

  * @throws IndexOutOfBoundsException {@inheritDoc}

  */

  public E get(int index) {

  //范围检查

  rangeCheck(index);

  return elementData(index);

  }

  查找的元素不在指定范围,抛出IndexOutOfBoundsException。

  private void rangeCheck(int index) {

  if (index >= size)

  throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

  }

  很明显ArrayList操作get的时候,每次都要去检查一遍数组角标,虽然仅仅只多了一个操作,但在连接池场景下,频繁的get、remove操作,还是会有明显的性能消耗。而FastList不做rangeCheck因此速度更快。

  2)FastList#remove

  @Override

  public boolean remove(Object element)

  {

  for (int index = size - 1; index >= 0; index--) {

  if (element == elementData[index]) {

  final int numMoved = size - index - 1;

  if (numMoved > 0) {

  System.arraycopy(elementData, index + 1, elementData, index, numMoved);

  }郑州妇科医院 http://www.120zzzy.com/

  elementData[--size] = null;

  return true;

  }

  }

  return false;

  }

  FastList#remove方法同理,仅仅少了rangeCheck,其他完全相同。

  3)FastList#add

  @Override

  public boolean add(T element)

  {

  //容器未满,直接赋值

  if (size < elementData.length) {

  elementData[size++] = element;

  }

  else {

  // 容器已满

  //elementData 初始化容量不够用 需要扩容 容量左移一位,相当于 容量*2

  final int oldCapacity = elementData.length;

  final int newCapacity = oldCapacity << 1;

  @SuppressWarnings("unchecked")

  //扩容-> 创建新数组

  final T[] newElementData = (T[]) Array.newInstance(clazz, newCapacity);

  //数组拷贝

  System.arraycopy(elementData, 0, newElementData, 0, oldCapacity);

  //重新赋值,即执行 add

  newElementData[size++] = element;

  elementData = newElementData;

  }

  return true;

  }

  ArrayList#add,扩容时,最终调用

  private void grow(int minCapacity) {

  //overflow-conscious code

  int oldCapacity = elementData.length;

  //扩容为原来的1.5倍

  int newCapacity = oldCapacity + (oldCapacity >> 1);

  if (newCapacity - minCapacity < 0)

  newCapacity = minCapacity;

  if (newCapacity - MAX_ARRAY_SIZE > 0)

  newCapacity = hugeCapacity(minCapacity);

  // minCapacity is usually close to size, so this is a win:

  elementData = Arrays.copyOf(elementData, newCapacity);

  }

  FastList#add方法,相比ArrayList扩容为原来的1.5倍,FastLis直接左移一位,扩容为原来2倍。而这次操作本身也少了一次 + 操作,更适应HikariCP的应用需求。

  【 oldCapacity + (oldCapacity >> 1); 与 oldCapacity << 1; 】。

  最终结论:FastList相比ArrayList仅仅是去掉了rage检查、扩容等细节处,微小的调整,从而追求性能极致。

  关于为什么ArrayList 要做rangeCheck?

  因为像FastList一样,不做check,直接操作底层数组,出现异常时一样抛出异常。

  我的理解:定位不同、追求不同,ArrayList作为通用容器,更追求安全、稳定,操作前rangeCheck检查,对非法请求直接抛出异常,更符合 fail-fast(快速失败)机制 。而FastList追求的是性能极致。

上一篇:访问者设计模式


下一篇:android-反射的使用(反射静态内部类、非静态内部类、匿名内部类等)