推荐使用 threadpoolexecutor 自定义线程池替代 executors 工厂方法,设置合理 core/maxpoolsize、有界队列、命名 threadfactory 和拒绝策略;http 客户端应复用实例并配置连接池与超时;耗时统计须保存原始数据以计算分位值;jvm 需固定堆大小、选用合适 gc 并开启 gc 日志;linux 需调高 ulimit -n。

用 ExecutorService 控制并发线程数,别直接 new Thread()
手动 new Thread() 启动几百个线程,很容易 OOM 或触发系统线程限制。Java 自带的 ExecutorService 能复用线程、控制队列、统一异常处理,是压测工具的合理起点。
常见错误:用 Executors.newCachedThreadPool() —— 它会无上限创建线程,高并发下瞬间崩掉;或者用 FixedThreadPool 但没设拒绝策略,任务堆积导致内存泄漏。
- 推荐用
new ThreadPoolExecutor(corePoolSize, maxPoolSize, keepAliveTime, TimeUnit.SECONDS, new ArrayBlockingQueue(queueSize), r -> new Thread(r, "load-test-" + counter.getAndIncrement()), new ThreadPoolExecutor.CallerRunsPolicy()) -
corePoolSize设为 CPU 核数 × 2(IO 密集型可更高),maxPoolSize控制峰值,queueSize别设太大(比如 100~500),避免请求积压掩盖真实响应瓶颈 - 必须指定
ThreadFactory命名线程,否则 jstack 查问题时全是pool-1-thread-xx,无法区分压测线程和其他业务线程
发 HTTP 请求别手写 socket,优先选 HttpClient(Apache)或 HttpClient(JDK 11+)
用 HttpURLConnection 写压测容易踩坑:连接不复用、超时默认值不合理、重定向自动跟随干扰统计、SSL 验证失败静默失败等。Apache HttpClient 或 JDK 11+ 的 java.net.http.HttpClient 更可控。
常见错误:每次请求都新建 HttpClient 实例 —— 连接池失效,DNS 解析、TCP 握手、TLS 协商全重复,QPS 上不去还耗资源。
立即学习“Java免费学习笔记(深入)”;
- Apache 版:全局单例
CloseableHttpClient,用PoolingHttpClientConnectionManager配置最大连接数(如setMaxTotal(200))、每个路由最大连接(如setDefaultMaxPerRoute(50)) - JDK 11+ 版:用
HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(5)).build(),注意它默认启用连接复用,但需确保HttpRequest的GET方法没带非法 header(比如空User-Agent可能被某些服务端拒收) - 无论哪种,务必显式设置
connectTimeout和readTimeout,否则慢请求会卡死线程池
统计结果时别只看平均值,ArrayList 存所有耗时再算分位数
压测报告里只写 “平均 RT:127ms” 几乎没用。实际用户感知的是 P95/P99,而平均值会被长尾拖偏。必须采集每个请求的精确耗时(单位毫秒),后期聚合计算。
常见错误:用 AtomicLong 累加总耗时 + 计数器,然后除一下得平均值 —— 这样完全丢弃了分布信息,也看不出抖动。
- 用线程安全的
CopyOnWriteArrayList<long></long>或更优:压测线程本地存ArrayList<long></long>,结束后合并到一个主列表(减少锁竞争) - 别在压测循环里做排序或分位计算 —— 严重拖慢吞吐量;等所有请求完成再用
Collections.sort()+ 下标取值(如list.get((int) (list.size() * 0.95))) - 记得记录失败请求数和错误类型(如
ConnectException、SocketTimeoutException、HTTP 5xx),它们比慢更关键
启动参数不调,-Xmx 和 GC 日志就白写了
压测工具自己跑崩了,根本测不出服务端瓶颈。JVM 默认堆太小(尤其在容器里),GC 频繁或直接 OutOfMemoryError: unable to create new native thread 是高频问题。
常见错误:只加了 -Xmx4g,但没配 -Xms4g,导致运行中频繁扩容;或用 UseParallelGC 在低延迟场景下反而加剧 STW。
- 堆大小建议固定:
-Xms4g -Xmx4g(根据机器内存按比例调整,别超物理内存 75%) - Java 8 推荐
-XX:+UseG1GC -XX:MaxGCPauseMillis=200;Java 17+ 可试-XX:+UseZGC(需确认 OS 支持) - 必加
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:gc.log,压测后第一件事就是看 GC 是否吃掉大量时间 - Linux 下还要检查
ulimit -n(文件描述符数),压测开几百连接时容易 hit limit,报IOException: Too many open files
真正难的不是写完这几十行代码,而是每次改一个参数(比如线程数、超时值、连接池大小)后,都要重新跑、比对 GC 日志、看错误率是否突变——这些细节不盯住,压测数据就只是幻觉。










