在Down-the-Rabbit-Hole中,作者提到了We're in your bytecodez的字节码优化
In order to make HikariCP as fast as it is, we went down to bytecode-level engineering, and beyond. We pulled out every trick we know to help the JIT help you. We studied the bytecode output of the compiler, and even the assembly output of the JIT to limit key routines to less than the JIT inline-threshold. We flattened inheritance hierarchies, shadowed member variables, eliminated casts.
字节码优化这块作者还提到了 this change removed a static field access, a push and pop from the stack, and made the invocation easier for the JIT to optimize because the callsite is guaranteed not to change.感兴趣的可以看一下 https://github.com/brettwooldridge/HikariCP/wiki/Down-the-Rabbit-Hole。
ProxyConnection(proxy class for java.sql.Connection)
ProxyStatement(proxy class for java.sql.Statement)
ProxyPreparedStatement(proxy class for java.sql.PreparedStatement)
ProxyCallableStatement(proxy class for java.sql.CallableStatement)
ProxyResultSet(proxy class for java.sql.ResultSet)
紧密结合以上五个代理类的还有两个类ProxyFactory(A factory class that produces proxies around instances of the standard JDBC interfaces)和JavassistProxyFactory(This class generates the proxy objects for {@link Connection}, {@link Statement},{@link PreparedStatement}, and {@link CallableStatement}.Additionally it injects method bodies into the {@link ProxyFactory} class methods that can instantiate instances of the generated proxies.)。
我们看一下ProxyFactory这个工厂类,大家是不是可以看到对上面的五个代理类提供的方法只有一行直接抛异常IllegalStateException的代码,并且提示你You need to run the CLI build and you need target/classes in your classpath to run。
注释写着“Body is replaced (injected) by JavassistProxyFactory”,其实方法body中的代码是在编译时调用JavassistProxyFactory才生成的。
package com.zaxxer.hikari.pool; import java.sql.CallableStatement; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.Statement; import com.zaxxer.hikari.util.FastList; /** * A factory class that produces proxies around instances of the standard * JDBC interfaces. * * @author Brett Wooldridge */ @SuppressWarnings("unused") publicfinalclassProxyFactory { privateProxyFactory() { // unconstructable } /** * Create a proxy for the specified {@link Connection} instance. * @param poolEntry the PoolEntry holding pool state * @param connection the raw database Connection * @param openStatements a reusable list to track open Statement instances * @param leakTask the ProxyLeakTask for this connection * @param now the current timestamp * @param isReadOnly the default readOnly state of the connection * @param isAutoCommit the default autoCommit state of the connection * @return a proxy that wraps the specified {@link Connection} */ static ProxyConnection getProxyConnection(final PoolEntry poolEntry, final Connection connection, final FastList<Statement> openStatements, final ProxyLeakTask leakTask, finallong now, finalboolean isReadOnly, finalboolean isAutoCommit) { // Body is replaced (injected) by JavassistProxyFactory thrownew IllegalStateException("You need to run the CLI build and you need target/classes in your classpath to run."); } static Statement getProxyStatement(final ProxyConnection connection, final Statement statement) { // Body is replaced (injected) by JavassistProxyFactory thrownew IllegalStateException("You need to run the CLI build and you need target/classes in your classpath to run."); } static CallableStatement getProxyCallableStatement(final ProxyConnection connection, final CallableStatement statement) { // Body is replaced (injected) by JavassistProxyFactory thrownew IllegalStateException("You need to run the CLI build and you need target/classes in your classpath to run."); } static PreparedStatement getProxyPreparedStatement(final ProxyConnection connection, final PreparedStatement statement) { // Body is replaced (injected) by JavassistProxyFactory thrownew IllegalStateException("You need to run the CLI build and you need target/classes in your classpath to run."); } static ResultSet getProxyResultSet(final ProxyConnection connection, final ProxyStatement statement, final ResultSet resultSet) { // Body is replaced (injected) by JavassistProxyFactory thrownew IllegalStateException("You need to run the CLI build and you need target/classes in your classpath to run."); } }
static ResultSet getProxyResultSet(final ProxyConnection connection, final ProxyStatement statement, final ResultSet resultSet) { // Body is replaced (injected) by JavassistProxyFactory thrownew IllegalStateException("You need to run the CLI build and you need target/classes in your classpath to run."); }
// ********************************************************************** // "Overridden" java.sql.Connection Methods // ********************************************************************** /** {@inheritDoc} */ @Override publicfinalvoidclose()throws SQLException { // Closing statements can cause connection eviction, so this must run before the conditional below closeStatements(); if (delegate != ClosedConnection.CLOSED_CONNECTION) { leakTask.cancel(); try { if (isCommitStateDirty && !isAutoCommit) { delegate.rollback(); lastAccess = currentTime(); LOGGER.debug("{} - Executed rollback on connection {} due to dirty commit state on close().", poolEntry.getPoolName(), delegate); } if (dirtyBits != 0) { poolEntry.resetConnectionState(this, dirtyBits); lastAccess = currentTime(); } delegate.clearWarnings(); } catch (SQLException e) { // when connections are aborted, exceptions are often thrown that should not reach the application if (!poolEntry.isMarkedEvicted()) { throw checkException(e); } } finally { delegate = ClosedConnection.CLOSED_CONNECTION; poolEntry.recycle(lastAccess); } } }
ProxyConnection如下图,可以看到该类是真正使用FastList来进行优化的 private final FastList openStatements;
// 用于标识连接被访问或存在可提交数据 finalvoidmarkCommitStateDirty() { if (isAutoCommit) { lastAccess = currentTime(); } else { isCommitStateDirty = true; } } // 缓存statement privatesynchronized <T extends Statement> T trackStatement(final T statement) { openStatements.add(statement); return statement; } // 移出statement缓存 finalsynchronizedvoiduntrackStatement(final Statement statement) { openStatements.remove(statement); } // 关闭全部已打开的statement(只在close方法中调用) @SuppressWarnings("EmptyTryBlock") privatesynchronizedvoidcloseStatements() { finalint size = openStatements.size(); if (size > 0) { for (int i = 0; i < size && delegate != ClosedConnection.CLOSED_CONNECTION; i++) { try (Statement ignored = openStatements.get(i)) { // automatic resource cleanup } catch (SQLException e) { LOGGER.warn("{} - Connection {} marked as broken because of an exception closing open statements during Connection.close()", poolEntry.getPoolName(), delegate); leakTask.cancel(); poolEntry.evict("(exception closing Statements during Connection.close())"); delegate = ClosedConnection.CLOSED_CONNECTION; } } openStatements.clear(); } }
In order to generate proxies for Connection, Statement, and ResultSet instances HikariCP was initially using a singleton factory, held in the case of ConnectionProxy in a static field (PROXY_FACTORY).