【问题标题】:identityHashCode difference between Java/ColdFusion and LuceeJava/ColdFusion 和 Lucee 之间的 identityHashCode 区别
【发布时间】:2021-06-02 15:05:42
【问题描述】:

编辑:Bug has been filed.

假设我有两个相互指向的 ArrayList(循环引用):

x = createObject("java", "java.util.ArrayList").init();
y = createObject("java", "java.util.ArrayList").init();
x.add(y);
y.add(x);

如果我在其中任何一个上调用hashCode,它会导致StackOverflowError due to the ArrayList implementation。这是意料之中的。

但是,当我调用System.identityHashCode 时,它不应该使用Object.hashCode 实现,它不会跟随ArrayList 中的元素,因此不会导致StackOverflowError

Documentation of identityHashCode 状态:

为给定对象返回与返回相同的哈希码 通过默认方法 hashCode(),无论给定对象的 类覆盖 hashCode()。

在 Adob​​e ColdFusion 中,此代码可以正常工作:

System = createObject("java", "java.lang.System");

System.identityHashCode(x); // returns some integer
System.identityHashCode(y); // returns another integer

(这显然也适用于本地编译并使用 Java 运行。)

然而,在 Lucee 中,它会立即导致 StackOverflowError:

lucee.runtime.exp.NativeException: java.lang.StackOverflowError
    at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:627)
    at java.base/java.util.ArrayList.hashCode(ArrayList.java:614)
    at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:627)
    at java.base/java.util.ArrayList.hashCode(ArrayList.java:614)
    [...]

为什么在这里运行hashCode的ArrayList实现?

两个 CFML 引擎在相同的 servlet (Tomcat 9) 上运行相同的 JVM (HotSpot) 和 Java 版本 (11)。我想了解为什么他们的行为不同。

【问题讨论】:

    标签: java arraylist coldfusion lucee


    【解决方案1】:

    System.identityHashCode 的实现是native - 它在 VM 级别实现;它不是java代码。 iHC 的规范是故意模糊的。

    之所以含糊不清,是因为它高度依赖于平台,并且该规范试图为异国平台上的虚拟机实施者提供足够的余地(ColdFusion 和 Lucee 肯定算数,不是吗?)以制作符合规范的 impl。

    Object 的 hashCode impl 扫描字段在技术上是可行的,尽管这样做效率非常低(因为 System.iHC 在需要快速响应的地方大量使用,而且绝不会如此),而且您不是唯一一个假设 System.iHC 不会永远循环的人,即使在(最终)引用自身的对象中也是如此。

    但是,关键是,那些被广泛使用的假设;该方法的规范中没有任何内容实际上表明它是这样工作的。

    另一方面,lucee 所采取的回旋余地(如果你说的确实是真的)相当过分。

    因此,您现在陷入困境。这些都是真的:

    • 大量代码假定 iHC 无法循环。因此,事实上,VM impl 会循环是非常不切实际的。
    • 大量代码假定 iHC 速度很快。因此,事实上,VM impl 会很慢是非常不切实际的。
    • 尽管如此,Lucee 可以走“我是橡胶,你是胶水,无论你报告什么错误报告都会从我身上反弹并粘在你身上”的路线,只是告诉你他们的 impl 根据规范是有效的,因此根据任何代码你愿意向他们折腾,以表明你的观点是错误的。

    但是先给他们一个机会,在你认为他们会打开敞开的门之前'nu uh!不是我们的错!技术上可供他们使用的路线。

    如果他们拒绝了您的错误报告,并且/或者您想在提交到他们的错误跟踪器之前对其进行改进,那么您可能希望调查以下事项:

    • 制作一个自引用对象并将其用作 IdentityHashMap 中的键。这个stackoverflow吗?这将是一个很好的领导,因为现在你向他们展示了这个问题的严重性:要么他们承认 java.util 中的核心类有错误,要么他们承认他们的代码有错误,或者他们采取更奇特的立场是,IHM 和 System.iHC 的规范结合起来得出结论,任何尝试使用自引用对象作为 IHM 中的键的代码都是错误的。如果他们不想接受这个错误,这可能就是他们最终的结果,所以要做好失望的准备。
    • 找到几个库并证明它们不能在 lucee 上工作。一个值得关注的地方是序列化库,它们声明它们支持自引用/克隆引用(例如包含自身的列表,或多次包含相同的 obj 引用)。
    • == 在 lucee 中的行为如何? new String("foo") == new String("foo") 是否等于 true?

    但最重要的是对 lucee 人保持耐心。由于他们所使用的平台的限制,他们完全有可能没有真正的方法可以实际实现 System.iHC,在这种情况下,他们无能为力,只能同情和耸耸肩。

    【讨论】:

    • 我检查了 Lucee 的源代码,发现它甚至没有达到identityHashCode。 ArrayLists 存储在Set 中,它调用contains 进行一些“清理”(?),触发ArrayList 的hashCode。所以我们开始了。为他们提交错误。感谢您的洞察力。
    猜你喜欢
    • 2012-01-21
    • 2016-06-29
    • 2012-09-03
    • 2016-03-28
    • 2017-12-23
    • 2012-02-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多