资源泄漏通常只在并发或异常路径下出现,普通的单元测试看不到。这篇用文件描述符、线程和连接池三种资源实测:什么样的测试能发现泄漏,什么样的测试会漏掉它或误报,以及 System.gc() 如何影响结论。
calls=3 单线程 成功3 失败0 fd基线6 之后6 增量0 | System.gc()后6 增量0 calls=300 单线程 成功258 失败42 fd基线6 之后48 增量42 | System.gc()后6 增量0 calls=2000 16线程 成功1715 失败285 fd基线6 之后291 增量285 | System.gc()后6 增量0
默认 GC:
calls=2000 16线程 成功1715 失败285 fd基线6 之后291 增量285 | System.gc()后6 增量0
calls=2000 16线程 成功1715 失败285 fd基线6 之后291 增量285 | System.gc()后6 增量0
calls=2000 16线程 成功1715 失败285 fd基线6 之后291 增量285 | System.gc()后6 增量0
-XX:+UseEpsilonGC:
calls=2000 16线程 成功1715 失败285 fd基线6 之后291 增量285 | System.gc()后291 增量285
calls=2000 16线程 成功1715 失败285 fd基线6 之后291 增量285 | System.gc()后291 增量285
calls=2000 16线程 成功1715 失败285 fd基线6 之后291 增量285 | System.gc()后291 增量285
fixed calls=2000 成功1715 失败285 fd基线6 之后6 增量0leaky n=1 线程基线6 之后8 增量2 | System.gc()后8 增量2 fixed n=1 线程基线8 之后8 增量0 leaky n=10 线程基线6 之后26 增量20 | System.gc()后26 增量20 fixed n=10 线程基线26 之后26 增量0 leaky n=50 线程基线6 之后106 增量100 | System.gc()后106 增量100 fixed n=50 线程基线106 之后106 增量0 leaky n=200 线程基线6 之后406 增量400 | System.gc()后406 增量400 fixed n=200 线程基线406 之后406 增量0
shutdown() 后不等待:每 50 次调用后立即测到的线程增量 [100, 200, 300, 400];最后一次调用后立即 400;再等到增量归零用了约 30 ms,归零后增量 0 shutdown() 后不等待:每 50 次调用后立即测到的线程增量 [100, 200, 300, 400];最后一次调用后立即 400;再等到增量归零用了约 30 ms,归零后增量 0 shutdown() 后不等待:每 50 次调用后立即测到的线程增量 [100, 200, 300, 400];最后一次调用后立即 400;再等到增量归零用了约 25 ms,归零后增量 0
泄漏版 200 次并发调用:成功64 失败136(其中拿不到连接而超时 126)空闲连接 0/10 之后再调用 5 次(都是不会失败的 i=1):成功0 超时5 修复版 200 次并发调用:成功172 失败28(其中拿不到连接而超时 5)空闲连接 10/10