Java中的零垃圾大字符串反序列化,庞大的对象问题
我正在寻找一种方法,在Java中从Java中的零垃圾大字符串反序列化,庞大的对象问题,java,serialization,garbage-collection,java-8,g1gc,Java,Serialization,Garbage Collection,Java 8,G1gc,我正在寻找一种方法,在Java中从字节[]反序列化字符串,并尽可能少地产生垃圾。因为我正在创建自己的序列化程序和反序列化程序,所以我可以完全自由地在服务器端(即序列化数据时)和客户端(即反序列化数据时)实现任何解决方案 通过迭代字符串的字符(String.charAt(I)),并将每个char(16位值)转换为2x 8位值,我成功地序列化字符串,而不会产生任何垃圾开销。关于这一点有一场很好的辩论。另一种方法是使用反射直接访问字符串的底层char[],但这超出了问题的范围 然而,如果不创建两次ch
字节[]
反序列化字符串
,并尽可能少地产生垃圾。因为我正在创建自己的序列化程序和反序列化程序,所以我可以完全自由地在服务器端(即序列化数据时)和客户端(即反序列化数据时)实现任何解决方案
通过迭代字符串的字符(String.charAt(I)
),并将每个char
(16位值)转换为2x 8位值,我成功地序列化字符串,而不会产生任何垃圾开销。关于这一点有一场很好的辩论。另一种方法是使用反射直接访问字符串的
底层char[]
,但这超出了问题的范围
然而,如果不创建两次char[]
,我似乎不可能对字节[]
进行反序列化,这看起来很奇怪
程序如下:
创建char[]
迭代字节[]
并填写字符[]
使用String(char[])
constructor创建字符串
由于Java的String
不变性规则,构造函数复制char[],从而产生2倍的GC开销。我总是可以使用一些机制来规避这个问题(不安全的String
allocation+Reflection来设置char[]
实例),但我只是想问一下,除了打破String的
不变性的每一个约定之外,是否还有其他后果
当然,对此最明智的回应是“来吧,停止这样做,相信GC,原来的char[]
将非常短暂,G1将立即摆脱它”,这实际上是有道理的,如果char[]
小于G1区域大小的1/2。如果较大,char[]将直接分配为一个庞大的对象(即自动传播到G1区域之外)。这样的对象在G1中很难被有效地垃圾收集。这就是为什么每次分配都很重要
关于如何解决这个问题有什么想法吗
非常感谢
这样的对象在G1中很难被有效地垃圾收集
这可能不再是事实,但您必须为自己的应用程序评估它。JDK的缺陷,并引入了新的机制来收集庞大、短暂的对象。根据bug标志,您可能必须使用jdk版本运行≥8u40和≥8u60以获得各自的好处
感兴趣的实验选项:
-XX:+G1ReclaimDeadHumongousObjectsAtYoungGC
追踪:
-XX:+G1TraceReclaimDeadHumongousObjectsAtYoungGC
有关这些功能的进一步建议和问题,我建议点击邮件列表。我找到了一个解决方案,如果您有一个非托管环境,它是无用的
java.lang.String
类有一个包私有构造函数String(char[]值,布尔共享)
资料来源:
/*
* 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
package并访问包私有构造函数。这对安全管理器来说不是个好兆头,但可以避免。找到了一个使用简单的“秘密”本机Java库的有效解决方案:
String longString = StringUtils.repeat("bla", 1000000);
char[] longArray = longString.toCharArray();
String fastCopiedString = SharedSecrets.getJavaLangAccess().newStringUnsafe(longArray);
你有没有考虑过不使用字符串,只是在绝对必要的时候序列化原始字节数据并在子部分上进行字符集转换?我有。我的想法是创建一个新类MutableString
,并在其上实现许多传统的垃圾重操作(例如,fastpathString
split),然后有一个方法toString(from,to)
,它创建一个String
类型的“视图”实例。我可以做到。但这需要完全重构我们的应用程序,并尽可能地使用MutableString
s。这是一个好主意,但我想先探索替代方案。你知道所有这些东西都已经存在了吗?有CharBuffer
和StringBuilder
,它们都是一种可变的String
(除非您创建了一个不可变的视图),有一些方法可以创建它们的轻量级子序列,它们都实现了CharSequence
,regex包所在的接口,它实际上实现了split
操作。在查看源代码时,在String
s、CharBuffer
s和StringBuilder
s之间进行转换时,字符内容似乎总是被复制的,HotSpot对它们进行了特殊的优化…谢谢,我将看一看,不必将类移动到包中,您可能只需通过反射访问construcor,然后为构造函数方法构建一个方法句柄/lambda,以避免调用开销