gpt4 book ai didi

java - 如何检测String.substring是否复制字符数据

转载 作者:塔克拉玛干 更新时间:2023-11-03 03:18:38 25 4
gpt4 key购买 nike

我知道对于 Oracle Java 1.7 update 6 和更新版本,当使用 String.substring 时,String 的内部字符数组被复制,对于旧版本,它是共享的。但是我发现没有官方 API 可以告诉我当前的行为。

用例

我的用例是:在解析器中,我喜欢检测 String.substring 是否复制或共享底层字符数组。问题是,如果字符数组是共享的,那么我的解析器需要使用 new String(s) 显式“取消共享”以避免内存问题。但是,如果 String.substring 无论如何都会复制数据,那么这就没有必要了,并且可以避免在解析器中显式复制数据。用例:

// possibly the query is very very large
String query = "select * from test ...";
// the identifier is used outside of the parser
String identifier = query.substring(14, 18);

// avoid if possible for speed,
// but needed if identifier internally
// references the large query char array
identifier = new String(identifier);

我需要什么

基本上,我想要一个静态方法 boolean isSubstringCopyingForSure() 来检测是否不需要 new String(..)。如果有 SecurityManager,即使检测不起作用,我也没关系。基本上,检测应该是保守的(为了避免内存问题,我宁愿使用 new String(..) 即使不是必要的)。

选项

我有几个选择,但我不确定它们是否可靠,特别是对于非 Oracle JVM:

检查 String.offset 字段

/**
* @return true if substring is copying, false if not or if it is not clear
*/
static boolean isSubstringCopyingForSure() {
if (System.getSecurityManager() != null) {
// we can not reliably check it
return false;
}
try {
for (Field f : String.class.getDeclaredFields()) {
if ("offset".equals(f.getName())) {
return false;
}
}
return true;
} catch (Exception e) {
// weird, we do have a security manager?
}
return false;
}

检查 JVM 版本

static boolean isSubstringCopyingForSure() {
// but what about non-Oracle JREs?
return System.getProperty("java.vendor").startsWith("Oracle") &&
System.getProperty("java.version").compareTo("1.7.0_45") >= 0;
}

检查行为有两种选择,两者都相当复杂。一种是使用自定义字符集创建一个字符串,然后使用子字符串创建一个新字符串 b,然后修改 原始字符串并检查 b 是否也被更改。第二个选项是创建巨大的字符串,然后是一些子字符串,并检查内存使用情况。

最佳答案

是的,确实是在 7u6 中进行了此更改。对此没有 API 更改,因为此更改严格来说是实现更改,而不是 API 更改,也没有 API 可以检测正在运行的 JDK 具有哪种行为。但是,由于更改,应用程序当然有可能注意到性能或内存利用率的差异。事实上,编写一个在 7u4 中运行但在 7u6 中失败的程序并不难,反之亦然。我们预计这种权衡对大多数应用程序都是有利的,但毫无疑问,有些应用程序会受到这种变化的影响。

有趣的是,您担心共享字符串值的情况(7u6 之前)。我听说的大多数人都有相反的担忧,他们喜欢共享和 7u6 更改为非共享值给他们带来了问题(或者,他们担心这会导致问题)。

无论如何,要做的是衡量,而不是猜测!

首先,比较有变化和没有变化的类似 JDK 之间的应用程序性能,例如7u4 和 7u6。可能您应该查看 GC 日志或其他内存监控工具。如果差异可以接受,那么您就完成了!

假设 7u6 之前的共享字符串值导致问题,下一步是尝试使用 new String(s.substring(...)) 的简单解决方法来强制字符串值取消共享。然后测量它。同样,如果两个 JDK 的性能都可以接受,那么您就完成了!

如果事实证明在未共享的情况下,对 new String() 的额外调用是 Not Acceptable ,那么检测这种情况并使“取消共享”调用成为条件的最佳方法可能是反射(reflect)一个字符串的 value 字段,它是一个 char[],并得到它的长度:

int getValueLength(String s) throws Exception {
Field field = String.class.getDeclaredField("value");
field.setAccessible(true);
return ((char[])field.get(s)).length;
}

考虑调用 substring() 产生的字符串,该字符串返回比原始字符串短的字符串。在共享的情况下,子字符串的 length() 将不同于如上所示检索的 value 数组的长度。在未共享的情况下,它们将是相同的。例如:

String s = "abcdefghij".substring(2, 5);
int logicalLength = s.length();
int valueLength = getValueLength(s);

System.out.printf("%d %d ", logicalLength, valueLength);
if (logicalLength != valueLength) {
System.out.println("shared");
else
System.out.println("unshared");

在早于 7u6 的 JDK 上,值的长度将为 10,而在 7u6 或更高版本上,值的长度将为 3。当然,在这两种情况下,逻辑长度均为 3。

关于java - 如何检测String.substring是否复制字符数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20260140/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com