Java中的零垃圾大字符串反序列化,Humongous对象问题
我正在寻找一种方法从Java中的byte[]
反序列化String
,尽可能少生成垃圾。 因为我正在创建自己的序列化器和反序列化器,所以我可以完全自由地在服务器端(即在序列化数据时)和客户端(即在反序列化数据时)实现任何解决方案。
我已经设法通过遍历String's
( String.charAt(i)
)并将每个char
(16位值)转换为2x 8位值,有效地序列化 String
而不会产生任何垃圾开销。 这里有一个很好的辩论。 另一种方法是使用Reflection直接访问String's
底层char[]
,但这超出了问题的范围。
但是,我似乎不可能在不创建char[]
两次的情况下反序列化byte[]
,这看起来很奇怪。
步骤:
- 创建
char[]
- 迭代
byte[]
并填写char[]
- 使用
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);