Java中的零垃圾大字符串反序列化,Humongous对象问题

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

我已经设法通过遍历String'sString.charAt(i) )并将每个char (16位值)转换为2x 8位值,有效地序列化 String而不会产生任何垃圾开销。 这里有一个很好的辩论。 另一种方法是使用Reflection直接访问String's底层char[] ,但这超出了问题的范围。

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

步骤:

  1. 创建char[]
  2. 迭代byte[]并填写char[]
  3. 使用String(char[])构造函数创建String

由于Java的String immutability规则,构造函数复制char [],创建2x GC开销。 我总是可以使用机制来规避这个(不安全String分配+reflection来设置char[]实例),但我只是想问一下除了我违反String's不变性的每个约定之外是否还有其他后果。

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

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

非常感谢。

这些物体很难在G1中有效地进行垃圾收集。

这可能不再适用,但您必须根据自己的应用对其进行评估。 JDK Bugs 8027959和8048179引入了收集大量短期物体的新机制。 根据bug标记,你可能需要运行jdk版本≥8u40和≥8u60才能获得各自的好处。

感兴趣的实验选项:

 -XX:+G1ReclaimDeadHumongousObjectsAtYoungGC 

跟踪:

 -XX:+G1TraceReclaimDeadHumongousObjectsAtYoungGC 

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

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

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)

解决方案(或黑客,无论你想要什么)都是将类移动到java.lang包并访问包私有构造函数。 这对安全经理来说不是好兆头,但这可以避免。

找到一个简单的“秘密”本机Java库的工作解决方案:

 String longString = StringUtils.repeat("bla", 1000000); char[] longArray = longString.toCharArray(); String fastCopiedString = SharedSecrets.getJavaLangAccess().newStringUnsafe(longArray);