我知道对于 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 是否也更改。第二个选项是创建巨大的字符串,然后是一些子字符串,并检查内存使用情况。