【问题标题】:Will .hashcode() return a different int due to compaction of tenure space?由于任期空间的压缩,.hashcode() 会返回不同的 int 吗?
【发布时间】:2011-04-17 08:06:13
【问题描述】:

如果我在某个对象上调用Object.hashcode() 方法,它会返回该对象的内部地址(默认实现)。这个地址是逻辑地址还是物理地址?

在垃圾回收中,由于内存压缩,对象在内存中发生移动。如果我在 GC 之前和之后调用 hashcode,它会返回相同的 hashcode(它返回)吗?如果是,那么为什么(因为压缩地址可能会改变)?

【问题讨论】:

  • 如果您打印几个Object.hashCodes 的值,您可能会注意到它们不太可能是地址。例如,任何合理实现的奇数。

标签: java garbage-collection


【解决方案1】:

link 中,它说默认哈希码确实是对象的 JVM 地址,但如果它被移动 - 地址保持一致。 我不知道这个来源有多可靠,但我确信这个方法的实现者考虑到了这种情况(这种情况并不少见或极端情况),并确保了这个方法的正确功能。

【讨论】:

    【解决方案2】:

    不,对象的默认哈希码不会改变。

    文档没有说哈希码地址,它说它是基于地址的。考虑哈希码是 32 位的,但有 64 位的 JVM。显然,直接使用地址并不总是有效。

    实现依赖于 JVM,但在 Sun (Oracle) JVM 中,我相信哈希码在第一次访问时会被缓存。

    【讨论】:

    • 来自hashCode的Java Doc:这通常通过将对象的内部地址转换为整数来实现
    • 实际上,当 GC 重定位一个对象时,hashcode 会被缓存……如果之前调用了hashcode()
    • 实际上 Ashish,javadoc 是这样说的:“这通常通过将对象的内部地址转换为整数来实现,但是 Java™ 编程语言不需要这种实现技术。 " 事实上,最近的 JVM 有一个命令行选项,允许您选择其他方法来生成哈希码。
    • 此外,“转换”意味着根本性的变化,而不是简单的、可逆的类型转换。
    【解决方案3】:

    如果散列码发生变化,该对象将在它插入的散列集中消失,Sun 将收到大量投诉。

    【讨论】:

      【解决方案4】:

      hashCode 的约定不能因为这样的原因而改变。

      【讨论】:

        【解决方案5】:

        @erickson 或多或少是正确的。 java.lang.Object.hashCode() 返回的哈希码在对象的生命周期内不会改变。

        这种(通常)实现的方式相当聪明。当一个对象被垃圾收集器重新定位时,它的原始哈希码必须存储在某个地方,以防再次使用它。实现这一点的明显方法是在对象头中添加一个 32 位字段来保存哈希码。但这会给每个对象增加 1 个字的开销,并且在最常见的情况下会浪费空间......在不调用对象的 hashCode 方法的情况下。

        解决方案是在对象的标志字中添加两个标志位,并(大致)如下使用它们。第一个标志在调用hashCode 方法时设置。第二个标志告诉hashCode 方法是使用对象的当前地址作为哈希码,还是使用存储的值。当 GC 运行并重定位对象时,它会测试这些标志。如果设置了第一个标志而未设置第二个标志,则 GC 在对象末尾分配一个额外的字并将原始对象位置存储在该字中。然后它设置两个标志。从那时起,hashCode 方法从对象末尾的单词中获取哈希码值。


        事实上,identityHashCode 实现必须这样做才能满足general hashCode contract 的以下部分:

        “只要在 Java 应用程序的执行过程中对同一个对象多次调用,hashCode 方法必须始终返回相同的整数,前提是对象上的 equals 比较中没有使用任何信息修改。这个整数不需要从一个应用程序的一次执行到同一应用程序的另一次执行保持一致。”

        identityHashCode() 的假设实现仅返回对象的 当前 机器地址,如果/当 GC 将对象移动到不同的地址时,将违反突出显示的部分。解决这个问题的唯一方法是(假设的)JVM 保证一旦调用了 hashCode 对象就永远不会移动。这会导致严重且难以处理的堆碎片问题。

        【讨论】:

        • 伟大的解释斯蒂芬!您对 hashCode() 工作的描述阐明了 hashCode() 如何在整个程序运行期间保留相同的值。同时,如果发生 GC+内存压缩,并且新对象(其 hashCode() 尚未被调用)被分配与旧对象相同的空间,那么 hashCode() 值不会与旧对象相同最初占用内存位置的活动对象?这对对象相等性和基于哈希的集合有何影响?
        • 我回答的第 3 段对此进行了解释。基本上,原始地址/哈希码在重定位时存储在对象的末尾。但仅在必要时;即仅当identityHashcode() 已被调用。
        • 我的意思是,Object1 的 hasCode 100 被复制到 Object1 末尾的多余单词中。此时假设发生了 GC 压缩,并且 Object1 被移动到其他地方,释放其原始内存位置以进行新的分配。假设由于某种巧合,新的 Object2 以某种方式分配在 Object1 的旧位置。 Object2 的 hashCode 是多少?不会是100吗? TH\his 将意味着 Object1(现在移动到其他地方,但在最后一个单词中保存了 hashCode 100)和 Object2(分配在 Object1 的旧位置)将共享相同的 hashCode!
        • @AshwinPrabhu - 是的,它会的。但这没关系。身份哈希码是哈希码......不是唯一标识符。
        • 在OpenJDK中,hashCode()是一个native method,与具体的JVM impl like HotSpot有关。在 Android 世界中,“向对象的标志字添加两个标志位”解决方案似乎是真的。即obj.shadow$_monitor_
        猜你喜欢
        • 2018-10-03
        • 1970-01-01
        • 2021-12-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多