连接池 ConnectionPool
ConnectionPool的get方法
- 从连接池中获取一个Connection并检查是否可用(
isEligible),可用则调用acquire。
-- 进入streamAllocation.acquire - 把从连接池中获取到的RealConnection对象赋给StreamAllocation的connection成员变量(
this.connection)。 - 把StreamAllocation的弱引用添加到RealConnection的allocations(ArrayList)集合中,通过这个集合判断StreamAllocation的个数,进而判断一个网络连接的负载量是否已经超过OkHttp指定的最大值(最大连接数)。
ConnectionPool的put方法
- 做完异步清理任务后,添加连接connection至connections(ArrayDeque)。
- cleanupRunnable用于回收connection(见下)。
- 每次http请求都会产生一个StreamAllocation对象,不管在哪个拦截器中产生的。
Connection的自动回收流程
- 利用GC回收算法
- 当连接(StreamAllocation)的数量变为0时,会被线程池ConnectionPool检测到
- ConnectionPool中有个独立的线程会开启cleanupRunnable来清理连接池。
-- 进入cleanupRunnable
while(true)死循环阻塞。- 首次清理会返回下次清理的间隔时间waitNanos。
cleanup中使用了GC回收算法。- 调用
ConnectionPool.this.wait等待释放锁和时间片,等待时间过后会再次调用cleanupRunnable去清理(while(true)),同时返回下次要清理的时间waitNanos。
-- 进入cleanup- 遍历队列中所有的RealConnection,类似Java中标记清除算法,首先标记出最不活跃的connection连接(空闲连接)。
- 若被标记的连接满足一定条件,如空闲的Socket连接超过5个,则将其(longestIdleConnection)从连接中删除,并关闭连接。
- 若全都是活跃连接(
idleConnectionCount > 0),则keepAliveDurationNs - longestIdleDurationNs时间后再次清理。 - 若有正在使用的连接(
inUseConnectionCount > 0),则返回keepAliveDurationNs时间供下次清理使用。 - 没有任何连接时返回-1,跳出
while(true)死循环。
- Q: 如何找到最不活跃的连接?
Ans:pruneAndGetAllocationCount方法遍历弱引用集合,根据弱引用是否为null,为null表示没有代码引用该对象,则删除它(references.remove(i))。
- 遍历队列中所有的RealConnection,类似Java中标记清除算法,首先标记出最不活跃的connection连接(空闲连接)。