【发布时间】:2013-11-28 07:28:54
【问题描述】:
我知道,对于 Oracle Java 1.7 更新 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 是否也被更改。第二个选项是创建巨大的字符串,然后是一些子字符串,并检查内存使用情况。
【问题讨论】:
-
如何创建一个字符串并检查是否为
string.substring(4, 6) == string.substring(4, 6)?您可以缓存此布尔结果并重用它。不完全确定它是否有效,只是一个想法。 -
可以和java.vendor系统属性结合使用
-
@Meagain 这不起作用。无论字符数组是否共享,子字符串都会返回一个新对象。
-
@benjamin.d 你当然是对的,我会更新这个问题。对于 Mac OS 上的 Java 1.6,供应商曾经是
Apple,但对于 Java 1.7,这应该不是问题。 -
让我重新考虑一下。我将发布代码。