Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/316.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 使用GSON JsonReader处理大型字段的最佳方法_Java_Gson_Out Of Memory - Fatal编程技术网

Java 使用GSON JsonReader处理大型字段的最佳方法

Java 使用GSON JsonReader处理大型字段的最佳方法,java,gson,out-of-memory,Java,Gson,Out Of Memory,我得到了一个java.lang.OutOfMemoryError:即使使用GSON流,java堆空间也是如此 {"result":"OK","base64":"JVBERi0xLjQKJeLjz9MKMSAwIG9iago8PC...."} base64最长可达200Mb。GSON占用的内存远不止此,(3GB)当我尝试将base64存储在变量中时,我得到: Exception in thread "main" java.lang.OutOfMemoryError: Java heap spac

我得到了一个java.lang.OutOfMemoryError:即使使用GSON流,java堆空间也是如此

{"result":"OK","base64":"JVBERi0xLjQKJeLjz9MKMSAwIG9iago8PC...."}
base64最长可达200Mb。GSON占用的内存远不止此,(3GB)当我尝试将base64存储在变量中时,我得到:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at java.util.Arrays.copyOf(Arrays.java:2367)
    at java.lang.AbstractStringBuilder.expandCapacity(AbstractStringBuilder.java:130)
    at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:114)
    at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:535)
    at java.lang.StringBuilder.append(StringBuilder.java:204)
    at com.google.gson.stream.JsonReader.nextQuotedValue(JsonReader.java:1014)
    at com.google.gson.stream.JsonReader.nextString(JsonReader.java:815)

处理此类字段的最佳方法是什么

之所以出现
OutOfMemoryError
是因为GSON
nextString()
返回一个字符串,该字符串在使用
StringBuilder
构建非常大的字符串时聚合。当您面临这样的问题时,您必须处理中间数据,因为没有其他选择。不幸的是,GSON不允许您以任何方式处理大量文本

不确定是否可以更改响应负载,但如果不能,可能需要实现自己的JSON读取器,或者“黑客”现有的
JsonReader
,使其以流式方式工作。下面的示例基于GSON 2.5,大量使用反射,因为
JsonReader
非常小心地隐藏其状态

EnhancedGson25JsonReader.java
final类EnhancedGson25JsonReader
扩展JsonReader{
//接受内部字符缓冲区的侦听器。
//接受建立在这样的缓冲区上的单个字符串也是完全的内存浪费。
接口ISlicedStringListener{
无效接受(字符[]缓冲区,整数开始,整数长度)
抛出IOException;
}
//这些常量可以直接复制
/**@see JsonReader#窥视#无*/
私有静态最终整型窥视_NONE=0;
/**@see JsonReader#窥视(单)引用*/
私有静态最终整型单引号=8;
/**@see JsonReader#窥视(双引号)*/
私有静态最终整型窥视双引号=9;
//下面是一组间谍,专门为父级的类状态“间谍”
私人侦探偷看;
非公开最终方法Spy doPeek;
私有最终方法Spy getLineNumber;
私有最终方法,包括列号;
专用最终FieldSpy缓冲区;
私人最终现场间谍pos;
私人最终现场监视限制;
私有final MethodSpy readEscapeCharacter;
专用最终FieldSpy线号;
私人决赛现场直播开始;
专用最终缓冲区;
私人最终方法间谍syntaxError;
私有最终字段大小;
私人最终字段索引;
专用增强器DSONReader(最终读取器)
抛出NoSuchFieldException、NoSuchMethodException{
超级(阅读器);
peek=spyField(JsonReader.class,此为“peek”);
doPeek=spyMethod(JsonReader.class,此为“doPeek”);
getLineNumber=spyMethod(JsonReader.class,这是“getLineNumber”);
getColumnNumber=spyMethod(JsonReader.class,这是“getColumnNumber”);
buffer=spyField(JsonReader.class,这个“buffer”);
pos=spyField(JsonReader.class,此为“pos”);
limit=spyField(JsonReader.class,此为“limit”);
readEscapeCharacter=spyMethod(JsonReader.class,此为“readEscapeCharacter”);
lineNumber=spyField(JsonReader.class,此为“lineNumber”);
lineStart=spyField(JsonReader.class,此为“lineStart”);
fillBuffer=spyMethod(JsonReader.class,这个“fillBuffer”,int.class);
syntaxError=spyMethod(JsonReader.class,这是“syntaxError”,String.class);
stackSize=spyField(JsonReader.class,此为“stackSize”);
PathIndexes=spyField(JsonReader.class,此为“PathIndexes”);
}
静态增强DSONReader getEnhancedGson25JsonReader(最终读取器){
试一试{
返回新的EnhancedJsonReader(reader);
}捕获(最终NoSuchFieldException | NoSuchMethodException ex){
抛出新的运行时异常(ex);
}
}
//此方法已从nextString()实现中复制和重写
void nextSlicedString(最终ISlicedStringListener侦听器)
抛出IOException{
int p=peek.get();
如果(p==偷看无){
p=doPeek.get();
}
开关(p){
案例单例报价:
nextQuotedSlicedValue('\'',侦听器);
打破
案例偷看双引号:
nextQuotedSlicedValue(“”,侦听器);
打破
违约:
抛出新的IllegalStateException(“应为字符串,但为”+peek()
+“at line”+getLineNumber.get()
+“column”+getColumnNumber.get()
+“路径”+getPath()
);
}
偷看。接受(没有偷看);
路径索引.get()[stackSize.get()-1]++;
}
//以下方法也是为“间谍”修补的复制粘贴。
//原则上,它与源缓冲区相同,但它还有一个缓冲区singleCharBuffer
//为了不向ISlicedStringListener接口添加其他方法(尽可能多地使用lamdba)。
//请注意,这两种方法之间的主要区别在于
//不聚合单个字符串值,但只委托内部
//缓冲区用于调用站点,因此后者可能会对缓冲区执行任何操作。
/**
*@see JsonReader#下一个QUOTEDVALUE(字符)
*/
私有void nextQuotedSlicedValue(最终字符引号,最终ISlicedStringListener侦听器)
抛出IOException{
final char[]buffer=this.buffer.get();
final char[]singleCharBuffer=新字符[1];
while(true){
int p=位置get();
int l=limit.get();
int start=p;
而(p<1){
final int c=缓冲区[p++];
if(c==引号){
pos.accept(p);
accept(buffer,start,p-start-1);
返回;
}else if(c=='\\'){
pos.accept(p);