java - Java 堆中存在许多 java.time.ZoneRegion 实例。难道 ZoneId 实例不应该被缓存吗?

标签 java zoneid

我有很多具有 ZoneId tz 字段的对象。 tz 字段的所有实例都是通过 ZoneId.of 静态方法创建的。 我最初选择 ZoneId 而不是普通的 String 是因为期望 ZoneId 实例将被缓存(因为时区集有限)。然而,在分析堆之后我发现事实并非如此:

enter image description here

每次使用相同参数调用 ZoneId.of 都会创建一个 ZoneRegion 的新实例:

ZoneId zoneId1 = ZoneId.of("Europe/Kiev"); //allocates new ZoneRegion
ZoneId zoneId2 = ZoneId.of("Europe/Kiev"); //allocates new ZoneRegion

我的问题是 - 这是预期的还是这是某种 JVM 错误?

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)

最佳答案

My question is - is that expected or this is some kind of JVM bug?

检查最新的 javadoc (JDK22u),没有提到“这些值是单例”。因为我们可以更简单地陈述您的情况,而不会引入堆问题:

// bug claim:
ZoneId a = ZoneId.of("Europe/Kiev");
ZoneId b = ZoneId.of("Europe/Kiev");
System.out.println(a == b); // should print true!

...但是这会打印false,因此这意味着驱动ZoneId.of的机制不会导致单例 ZoneId 对象 - 要么 JDK 不存在预缓存每个可能的 ZoneId 对象,要么 ZoneId.of 本身无法建立缓存。

or this is some kind of JVM bug?

好吧,Java Lang 规范和 Java 虚拟机规范不会(或不应该)提及任何有关此的内容,因为它不在 java.lang 包或子包中,这就是对语言本身至关重要的任何类型都需要在 JLS/JVMS 中提及的地方。

因此,javadoc 是规范的规范来源。

ZoneId 在 javadoc 中的任何地方都没有提到它们是单例,ZoneId.of 也没有提到这一点。

仅供引用,current JDK22u sources of ZoneId .

有一行:

 * The ID is unique within the system.

关于 ZoneId 本身,可能解释为:这里的“ID”是“ZoneId 的实例”的缩写,但这并不是对该行的明显解释;他们正在谈论的是例如Europe/Kiev 作为一个概念,不能导致同时指代哪个时区的两种不同概念。文档的这一部分与“...并且系统应缓存此概念以便不需要存在两个实例”的实现细节级别概念无关。

确实包含一个附加条件,即实例将被视为 ValueBased(在主 javadoc 的末尾):您不应该以任何方式做出假设,例如== 就可以(换句话说,他们明确保留引入某种缓存机制的权利,或者这些将成为直接的 valhalla 风格值类,没有 首先是一个身份)。但这只是规定您不应该依赖任何一个:您不能依赖重复调用 ZoneId.of("Europe/Kiev") 返回不同的对象,您也不能依赖此类调用返回相同的对象。从代码角度来看,您根本不应该考虑对象的 ZoneId 实例。

解决方案

如果您的探查器报告强烈建议减少 ZoneId 实例的数量会有所帮助,那么 API 文档将授予您缓存这些实例的权利:

private static final Map<String, ZoneId> CACHED_ZONE_IDS = new HashMap<>();

public static ZoneId cachedOf(String id) {
  return CACHED_ZONE_IDS.computeIfAbsent(id, ZoneId::of);
}

如果您担心需要“超时”缓存 map 中不再有效使用的 ZoneId,您可以查看 guava 的 CacheBuilder,但这似乎有点矫枉过正;我将其保留为上面的 3 行短代码。

关于java - Java 堆中存在许多 java.time.ZoneRegion 实例。难道 ZoneId 实例不应该被缓存吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/77660349/

相关文章:

java - 如何使用 Java ZoneId 的区域设置?

java - JSP 不能在 Java 资源中使用类

java - Android 增加倒计时器

java - 从 web.xml 迁移 servletMapping 时出现 Spring MVC JavaConfig 问题

java - Maven 插件仅在用户偏好时运行

java - 使用 Spring AOP 触发了哪个接口(interface)(扩展 CrudRepository)的删除方法?

java - ZoneOffset ZoneId 自定义序列化