Java 中的零垃圾大字符串反序列化,巨大的对象问题
Zero-garbage large String deserialization in Java, Humongous object issue
我正在寻找一种从 Java 中的 byte[]
反序列化 String
的方法,同时尽可能少地产生垃圾。因为我正在创建自己的序列化器和反序列化器,所以我可以完全自由地在服务器端(即序列化数据时)和客户端(即反序列化数据时)实施任何解决方案。
我通过遍历 String's
个字符(String.charAt(i)
)和将每个 char
(16 位值)转换为 2x 8 位值。关于这个 here 有一场很好的辩论。另一种方法是使用反射直接访问底层 char[]
的 String's
,但这超出了问题的范围。
但是,我似乎不可能在不创建 char[]
两次 的情况下反序列化 byte[]
,这看起来很奇怪。
程序:
- 创建
char[]
- 遍历
byte[]
并填写 char[]
- 使用
String(char[])
构造函数创建字符串
由于 Java 的 String
不变性规则,构造函数复制 char[],产生 2 倍的 GC 开销。我总是可以使用机制来规避这种情况(不安全 String
分配 + 设置 char[]
实例的反射),但我只是想问一下除了我打破关于 String's
不变性。
当然,对此最明智的回应是"come on, stop doing this and have trust in GC, the original char[]
will be extremely short-lived and G1 will get rid of it momentarily",这实际上是有道理的,如果char[]
小于G1区域大小的1/2。如果它更大,char[] 将被直接分配为一个巨大的对象(即自动传播到 G1 区域之外)。此类对象极难在 G1 中进行有效的垃圾回收。这就是每次分配都很重要的原因。
关于如何解决这个问题有什么想法吗?
非常感谢。
Such objects are extremely hard to be efficiently garbage collected in G1.
这可能不再正确,但您必须针对自己的应用程序对其进行评估。 JDK 错误 8027959 and 8048179 引入了用于收集巨大的、短暂的对象的新机制。根据错误标记,您可能必须 运行 使用 jdk ≥8u40 和 ≥8u60 的版本才能获得各自的好处。
感兴趣的实验选项:
-XX:+G1ReclaimDeadHumongousObjectsAtYoungGC
追踪:
-XX:+G1TraceReclaimDeadHumongousObjectsAtYoungGC
有关这些功能的进一步建议和问题,我建议您访问 hotspot-gc-use 邮件列表。
我找到了一个解决方案,但如果您有一个非托管环境,它就没用了。
java.lang.String
class 有一个包私有的构造函数 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)
。
解决方案(或黑客,无论你想怎么称呼它)是将 class 移动到 java.lang
包并访问包私有构造函数。这对安全管理器来说不是好兆头,但这可以避免。
使用简单的 "secret" 本机 Java 库找到了可行的解决方案:
String longString = StringUtils.repeat("bla", 1000000);
char[] longArray = longString.toCharArray();
String fastCopiedString = SharedSecrets.getJavaLangAccess().newStringUnsafe(longArray);
我正在寻找一种从 Java 中的 byte[]
反序列化 String
的方法,同时尽可能少地产生垃圾。因为我正在创建自己的序列化器和反序列化器,所以我可以完全自由地在服务器端(即序列化数据时)和客户端(即反序列化数据时)实施任何解决方案。
我通过遍历 String's
个字符(String.charAt(i)
)和将每个 char
(16 位值)转换为 2x 8 位值。关于这个 here 有一场很好的辩论。另一种方法是使用反射直接访问底层 char[]
的 String's
,但这超出了问题的范围。
但是,我似乎不可能在不创建 char[]
两次 的情况下反序列化 byte[]
,这看起来很奇怪。
程序:
- 创建
char[]
- 遍历
byte[]
并填写char[]
- 使用
String(char[])
构造函数创建字符串
由于 Java 的 String
不变性规则,构造函数复制 char[],产生 2 倍的 GC 开销。我总是可以使用机制来规避这种情况(不安全 String
分配 + 设置 char[]
实例的反射),但我只是想问一下除了我打破关于 String's
不变性。
当然,对此最明智的回应是"come on, stop doing this and have trust in GC, the original char[]
will be extremely short-lived and G1 will get rid of it momentarily",这实际上是有道理的,如果char[]
小于G1区域大小的1/2。如果它更大,char[] 将被直接分配为一个巨大的对象(即自动传播到 G1 区域之外)。此类对象极难在 G1 中进行有效的垃圾回收。这就是每次分配都很重要的原因。
关于如何解决这个问题有什么想法吗?
非常感谢。
Such objects are extremely hard to be efficiently garbage collected in G1.
这可能不再正确,但您必须针对自己的应用程序对其进行评估。 JDK 错误 8027959 and 8048179 引入了用于收集巨大的、短暂的对象的新机制。根据错误标记,您可能必须 运行 使用 jdk ≥8u40 和 ≥8u60 的版本才能获得各自的好处。
感兴趣的实验选项:
-XX:+G1ReclaimDeadHumongousObjectsAtYoungGC
追踪:
-XX:+G1TraceReclaimDeadHumongousObjectsAtYoungGC
有关这些功能的进一步建议和问题,我建议您访问 hotspot-gc-use 邮件列表。
我找到了一个解决方案,但如果您有一个非托管环境,它就没用了。
java.lang.String
class 有一个包私有的构造函数 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)
。
解决方案(或黑客,无论你想怎么称呼它)是将 class 移动到 java.lang
包并访问包私有构造函数。这对安全管理器来说不是好兆头,但这可以避免。
使用简单的 "secret" 本机 Java 库找到了可行的解决方案:
String longString = StringUtils.repeat("bla", 1000000);
char[] longArray = longString.toCharArray();
String fastCopiedString = SharedSecrets.getJavaLangAccess().newStringUnsafe(longArray);