从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追求的是性能极致。