Reflection String.intern()在JDBC驱动程序中返回不同的值

Reflection String.intern()在JDBC驱动程序中返回不同的值,reflection,kotlin,Reflection,Kotlin,当我试图制作一个假的JDBC驱动程序来测试安全类加载器时,我发现以下代码有一个奇怪的行为: val stringClass = java.lang.String::class.java val intern = stringClass.getMethod("intern") val pooledString = intern.invoke("Hello World") as String val valueField = stringClass.getDeclaredField("value")

当我试图制作一个假的JDBC驱动程序来测试安全类加载器时,我发现以下代码有一个奇怪的行为:

val stringClass = java.lang.String::class.java
val intern = stringClass.getMethod("intern")
val pooledString = intern.invoke("Hello World") as String
val valueField = stringClass.getDeclaredField("value")
valueField.isAccessible = true
val pooledValue = valueField.get(pooledString) as ByteArray
println(
        """|----------------------------------------
           | String: ${System.identityHashCode(stringClass)}
           | Thread: ${Thread.currentThread()}
           | Pooled: ${System.identityHashCode(pooledString)}
           | Internal: ${System.identityHashCode(pooledValue)}
           |----------------------------------------""".trimMargin()
)
for (index in pooledValue.indices) {
    pooledValue[index] = 'X'.toByte()
}
从JDBC驱动程序的伴生对象运行上述代码可以得到以下结果:

String: 349885916
Thread: Thread[main,5,main]
Pooled: 718231523
Internal: 1349414238
String: 349885916
Thread: Thread[main,5,main]
Pooled: 1635756693
Internal: 504527234
但在加载JDBC驱动程序之前(在程序的同一执行期间),从测试类的方法运行相同的代码会产生以下结果:

String: 349885916
Thread: Thread[main,5,main]
Pooled: 718231523
Internal: 1349414238
String: 349885916
Thread: Thread[main,5,main]
Pooled: 1635756693
Internal: 504527234
我本以为获取字符串的interned版本应该在这两种情况下给出相同的字符串,但似乎即使在程序的同一次运行中,这两个位置也为string.intern提供了不同的值,这与javadoc冲突,javadoc说:

调用intern方法时,如果池已包含 字符串等于此字符串对象,由 使用equals(Object)方法,则池中的字符串为 返回。否则,此字符串将添加到 池,并返回对此字符串的引用


这是预期的吗?如果是,为什么值不同?

很可能引用已被垃圾收集。合同规定,如果您两次获得相同的字符串并比较返回的字符串,它们将相等。但是,如果您获得一个字符串,请释放引用(使池中的字符串可用于gc),然后再次获得一个类似的字符串,没有任何东西可以保证它仍然会被池中。您将得到一个新的池字符串,该字符串具有不同的identityHashCode


保留对pooledString的引用,以便它不能被垃圾收集,并查看发生了什么

我尝试在JDBC驱动程序的伴生对象中保留一个引用,但行为没有改变。请执行此测试。第一次运行测试代码段时,将pooledString存储在s中(保存在某处)。然后,在第二次运行代码段时,打印s的哈希代码以及新pooledString的哈希代码以及布尔值pooledString==s。发生了什么?我正在打印系统标识码,您希望我在这两种环境中也重复吗?我不确定我是否理解。我以为你在启动一个程序来打印代码,把pooledString的引用保存在某个地方,这样它就不能是gc:d,然后再次打印代码并得到另一个结果?如果是这样,请同时在两个位置打印已保存对象的代码,以及已保存对象和已插入对象之间的相等性检查。如果两次运行程序,两次运行之间的不同标识码是正常的。程序的同一次运行,1来自JDBC驱动程序,另一个来自主类,主类1获得预期的标识码(修改以后使用的字符串),然而,在JDBC驱动程序中,返回的字符串与预期的不同。为了确保我正确地理解您…您正在侵入字符串内部,覆盖字符串的数组,然后,下一次对字符串池的查找没有找到此字符串,您会感到惊讶吗?我会感到惊讶的是,在同一线程的两种情况下,string.intern()返回不同的对象。执行查找不仅意味着散列,还意味着比较实际内容,您甚至会引用“由equals(Object)方法确定”。您正在操纵该内容,创建不一致的状态,导致不一致的行为。你期待什么?