高可用系统依赖对jvm内存与并发工具的深度理解:g1适合≥4gb堆,小堆用parallelgc;concurrenthashmap精确计数用mappingcount();线程池禁用executors,io密集型配synchronousqueue+callerrunspolicy;volatile不保证原子性,复合操作需锁或longadder。

高可用系统不是靠堆配置或加锁堆出来的,而是靠对 JVM 内存行为和 java.util.concurrent 工具链的因果级理解——错配 GC 策略比写错一个 synchronized 块更容易拖垮整套服务。
为什么 G1 在 4GB 堆上表现好,却在 200MB 场景下反而更卡
因为 G1 的 Region 划分有最小粒度(默认 1MB~32MB),堆太小会导致 Region 数过少,无法有效并发回收;同时 G1 的 Remembered Set 维护开销在小堆中占比陡增。实测中,-Xmx256m -XX:+UseG1GC 的 STW 时间可能比 -XX:+UseParallelGC 高 3 倍以上。
实操建议:
- 堆 ≤ 512MB:优先用
-XX:+UseParallelGC,吞吐优先且稳定 - 堆 ≥ 4GB 且延迟敏感(如 API 网关):启用
-XX:+UseG1GC,并设-XX:MaxGCPauseMillis=200和-XX:G1HeapRegionSize=2m(避免过大 Region 导致回收不均) - 堆 ≥ 16GB + 金融/订单类强实时场景:直接上
-XX:+UseZGC,但必须确认 JDK 版本 ≥ 11,且 OS 支持mmap大页映射(Linux 下需echo 1 > /proc/sys/vm/transparent_hugepage/enabled)
ConcurrentHashMap 的 size() 为什么不准,以及怎么安全取总数
ConcurrentHashMap.size() 返回的是估算值——它不加锁遍历所有 Segment 或 Node,只读 volatile 计数器,中间可能有写入未同步。压测中常见返回值比实际少 1~3 个元素,尤其在高并发 put/remove 时。
实操建议:
- 仅用于监控或日志打点:直接用
size()没问题,开销低 - 需要精确计数(如库存校验):改用
mappingCount()(JDK 8+),它基于sumCount()聚合所有 counterCells,精度高但耗时略长(毫秒级) - 极端高频读+低频写场景:可搭配
LongAdder单独维护逻辑总数,避免每次查 map
ThreadPoolExecutor 的拒绝策略选错了,会静默丢任务
很多人用 Executors.newFixedThreadPool(10),背后是 LinkedBlockingQueue 无界队列 + AbortPolicy。但一旦上游流量突增,队列无限堆积,内存先爆,最后 OOM —— 此时拒绝策略根本没机会触发。
实操建议:
- 永远不用
Executors工厂方法创建线程池,显式构造ThreadPoolExecutor - IO 密集型任务(如 HTTP 调用、DB 查询):核心线程数 =
2 * CPU核心数,队列用SynchronousQueue(零容量,逼迫线程池扩容或触发拒绝) - 拒绝策略优先选
CallerRunsPolicy:让调用线程自己执行任务,天然限流,不会丢任务也不会爆炸 - 监控必加:
executor.getQueue().size()和executor.getActiveCount(),阈值告警(如队列 > 100 或活跃线程 > 核心数 × 1.5)
volatile 和 synchronized 不是互斥选项,而是不同层级的语义工具
volatile 只保证可见性和禁止重排序,不保证原子性;synchronized 保证原子性+可见性+有序性。但很多人误以为“加了 volatile 就线程安全”,结果在 counter++ 这种复合操作上翻车。
实操建议:
- 单变量读写(如开关标志
isRunning):用volatile足够,轻量 - 多变量关联变更(如状态 + 时间戳):必须用
synchronized或ReentrantLock包裹整个逻辑块 - 高并发计数/累加:跳过锁,直接用
LongAdder(JDK 8+),它内部用 cell 分片 + CAS,比AtomicLong在 8 线程以上场景快 3~5 倍 - 注意
volatile不能修饰long或double数组元素——数组引用是 volatile 的,但元素不是
真正难的从来不是记住哪个参数该设多少,而是当 OutOfMemoryError: Metaspace 和 RejectedExecutionException 同时出现在日志里时,能立刻判断出是类加载泄漏引发 GC 压力,进而导致线程池拒绝——这种链路级归因能力,才是中高级工程师的分水岭。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











