9

我正在寻找一种方法来反序列化Java 中String的 abyte[]并尽可能少地产生垃圾。因为我正在创建自己的序列化器和反序列化器,所以我可以完全自由地在服务器端(即序列化数据时)和客户端(即反序列化数据时)实现任何解决方案。

通过遍历字符(_ String_ _ 这里有一个很好的辩论。另一种方法是使用反射直接访问底层,但这超出了问题的范围。String'sString.charAt(i)charString'schar[]

但是,我似乎不可能在byte[]不创建char[] 两次的情况下反序列化,这似乎很奇怪。

步骤:

  1. 创造char[]
  2. 遍历byte[]并填写char[]
  3. String(char[])使用构造函数创建字符串

由于 Java 的String不变性规则,构造函数会复制 char[],从而产生 2 倍的 GC 开销。我总是可以使用机制来规避这一点(不安全String的分配 + 反射来设置char[]实例),但我只是想问一下,除了打破关于String's不变性的所有约定之外,这是否有任何后果。

当然,对此最明智的回应是“来吧,停止这样做并信任 GC,原始char[]将非常短暂,G1 将暂时摆脱它”,这实际上是有道理的,如果char[]较小超过 G1 区域大小的 1/2。如果它更大,char[] 将被直接分配为一个巨大的对象(即自动传播到 G1 的区域之外)。在 G1 中,此类对象极难有效地进行垃圾回收。这就是为什么每个分配都很重要。

关于如何解决这个问题的任何想法?

非常感谢。

4

3 回答 3

4

在 G1 中,此类对象极难有效地进行垃圾回收。

这可能不再适用,但您必须针对自己的应用程序对其进行评估。JDK 错误80279598048179引入了新的机制来收集巨大的、短暂的对象。根据错误标志,您可能必须使用 ≥8u40 和 ≥8u60 的 jdk 版本运行才能获得各自的好处。

感兴趣的实验选项:

-XX:+G1ReclaimDeadHumongousObjectsAtYoungGC

追踪:

-XX:+G1TraceReclaimDeadHumongousObjectsAtYoungGC

有关这些功能的进一步建议和问题,我建议您点击hotspot-gc-use邮件列表。

于 2015-01-21T10:55:03.787 回答
1

我找到了一个解决方案,如果您有一个非托管环境,它是无用的。

该类java.lang.String有一个包私有构造函数String(char[] value, boolean share)

资源:

/*
* Package private constructor which shares value array for speed.
* this constructor is always expected to be called with share==true.
* a separate constructor is needed because we already have a public
* String(char[]) constructor that makes a copy of the given char[].
*/
String(char[] value, boolean share) {
    // assert share : "unshared not supported";
    this.value = value;
}

这在 Java 中被广泛使用,例如在Integer.toString(), Long.toString(), String.concat(String), String.replace(char, char), 中String.valueOf(char)

解决方案(或hack,无论您想如何称呼它)是将类移动到java.lang包并访问包私有构造函数。这对安全管理器来说不是好兆头,但这可以被规避。

于 2015-01-22T10:34:17.297 回答
0

使用简单的“秘密”本机 Java 库找到了一个可行的解决方案:

String longString = StringUtils.repeat("bla", 1000000);
char[] longArray = longString.toCharArray();
String fastCopiedString = SharedSecrets.getJavaLangAccess().newStringUnsafe(longArray);
于 2018-07-15T19:03:47.207 回答