You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java堆中大量ZoneRegion实例,ZoneId.of未缓存是预期还是JVM bug?

问题分析与解答

问题背景

我有大量包含ZoneId tz字段的对象,所有tz字段实例均通过ZoneId.of静态方法创建。选择ZoneId而非普通String,是因为预期ZoneId实例会被缓存(毕竟时区数量有限)。但堆分析显示,每次使用相同参数调用ZoneId.of都会创建新的ZoneRegion实例:

ZoneId zoneId1 = ZoneId.of("Europe/Kiev"); // 分配新的ZoneRegion
ZoneId zoneId2 = ZoneId.of("Europe/Kiev"); // 分配新的ZoneRegion

使用的JDK版本:

openjdk version "21.0.1" 2023-10-17
OpenJDK Runtime Environment (build 21.0.1+12-Ubuntu-222.04)
OpenJDK 64-Bit Server VM (build 21.0.1+12-Ubuntu-222.04, mixed mode, sharing)

结论

这种情况不是JVM bug,而是过时时区别名导致的非预期缓存失效。

原因说明

JDK的ZoneId确实会对标准时区ID进行全局缓存,但你使用的Europe/Kiev是过时的时区别名(对应标准ID为Europe/Kyiv)。在处理这类别名时,ZoneId.of()会完成别名到标准ID的解析,但返回的ZoneRegion实例并不会被加入全局缓存,因此每次调用都会创建新对象。

验证与解决

验证代码

// 过时别名:每次调用生成新实例
ZoneId zoneId1 = ZoneId.of("Europe/Kiev");
ZoneId zoneId2 = ZoneId.of("Europe/Kiev");
System.out.println(zoneId1 == zoneId2); // 输出 false

// 标准ID:复用缓存实例
ZoneId zoneId3 = ZoneId.of("Europe/Kyiv");
ZoneId zoneId4 = ZoneId.of("Europe/Kyiv");
System.out.println(zoneId3 == zoneId4); // 输出 true

解决办法

  1. 改用标准时区ID:通过ZoneId.getAvailableZoneIds()获取当前所有有效标准时区ID,替换代码中的过时别名;
  2. 自定义缓存:如果必须保留过时别名,可自行实现缓存层,例如用ConcurrentHashMap缓存通过别名创建的ZoneId实例:
private static final Map<String, ZoneId> ZONE_ID_CACHE = new ConcurrentHashMap<>();

public static ZoneId getCachedZoneId(String zoneId) {
    return ZONE_ID_CACHE.computeIfAbsent(zoneId, ZoneId::of);
}

内容的提问来源于stack exchange,提问作者Dmitriy Dumanskiy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 00:22:25