【问题标题】:GSON Serialization very very slowGSON 序列化非常非常慢
【发布时间】:2012-05-28 11:01:55
【问题描述】:

我正在尝试使用 GSON 序列化一个包含 7000 个 POJO 的数组,并且序列化时间非常慢。序列化以下对象的数组大约需要 3-5 秒:

public class Case {
    private Long caseId;
    private Key<Organization> orgKey;

    private Key<Workflow> workflowKey;
    private Key<User> creatorKey;

    private Date creationTimestamp;
    private Date lastUpdatedTimestamp;

    private String name;
    private String stage;
    private String notes;
}

使用自定义序列化器/反序列化器对关键字段进行序列化:

public class GsonKeySerializerDeserializer implements JsonSerializer<Key<?>>, JsonDeserializer<Key<?>>{

@Override
public JsonElement serialize(Key<?> src, Type typeOfSrc, JsonSerializationContext arg2) {
    return new JsonPrimitive(src.getString());
}

@Override
public Key<?> deserialize(JsonElement src, Type typeOfSrc, JsonDeserializationContext arg2) throws JsonParseException {
    if (src.isJsonNull() || src.getAsString().isEmpty()) {
        return null;
    }

    String s = src.getAsString();
    com.google.appengine.api.datastore.Key k = KeyFactory.stringToKey(s);
    return new Key(k);
}
}

为了测试手动编写 JSON 序列化程序的性能,我测试了以下代码,它序列化相同的 Case 对象数组的速度比 GSON 快大约 10 倍。

List<Case> cases = (List<Case>) retVal;
JSONArray a = new JSONArray();
for (Case c : cases) {
    JSONObject o = new JSONObject();
    o.put("caseId", c.getCaseId());
    o.put("orgKey", c.getOrgKey().getString());
    o.put("workflowKey", c.getWorkflowKey().getString());
    o.put("creatorKey", c.getCreatorKey().getString());
    o.put("creationTimestamp", c.getCreationTimestamp().getTime());
    o.put("lastUpdatedTimestamp", c.getLastUpdatedTimestamp().getTime());
    o.put("name", c.getName());
    o.put("stage", c.getStage());
    o.put("notes", c.getNotes());
    a.put(o);

}
String json = a.toString();

你知道为什么 GSON 在这种情况下表现如此糟糕吗?

更新

下面是实际开始序列化的代码:

Object retVal = someFunctionThatReturnsAList();
String json = g.toJson(retVal);
resp.getWriter().print(json);

更新2

这是一个非常简单的测试用例,说明了相对于 org.json 的较差性能:

List<Foo> list = new ArrayList<Foo>();
for (int i = 0; i < 7001; i++) {
    Foo f = new Foo();
    f.id = new Long(i);
    list.add(f);
}

Gson gs = new Gson();
long start = System.currentTimeMillis();
String s = gs.toJson(list);
System.out.println("Serialization time using Gson: " + ((double) (System.currentTimeMillis() - start) / 1000));


start = System.currentTimeMillis();
JSONArray a = new JSONArray();
for (Foo f : list) {
    JSONObject o = new JSONObject();
    o.put("id", f.id);
    a.put(o);

}
String json = a.toString();
System.out.println("Serialization time using org.json: " + ((double) (System.currentTimeMillis() - start) / 1000));

System.out.println(json.equals(s));

Foo 在哪里:

public class Foo {
public Long id;
}

这个输出:

Serialization time using Gson: 0.233
Serialization time using org.json: 0.028
true

几乎 10 倍的性能差异!

【问题讨论】:

  • 您使用的是哪个 gson 版本以及生成输出的代码是什么?
  • 我正在使用 gson 2.2,用代码更新问题以产生输出
  • 如何创建 Gson 对象?
  • GsonBuilder gson = new GsonBuilder(); gson.addSerializationExclusionStrategy(new GsonExclusionStrategy()); gson.registerTypeHierarchyAdapter(Key.class, new GsonKeySerializerDeserializer()); gson.registerTypeHierarchyAdapter(Date.class, new GsonDateSerializerDeserializer()); gson.registerTypeHierarchyAdapter(BlobKey.class, new GsonBlobKeySerializerDeserializer());返回 gson.create();
  • @aloo 是的,GSON 需要很长时间来初始化,我猜序列化/反序列化还可以,节省了编码时间,而不是运行时间,但是 GSON 的初始化很慢。您找到解决方案了吗?

标签: java json performance serialization gson


【解决方案1】:

我试图重现您的问题,但无法重现。我创建了 7000 个包含重要数据的对象。在我的 ThinkPad 上,Gson 需要约 260 毫秒来序列化约 3MB 的 Gson,这是一个可观的约 10Mbps。

大部分时间用于将日期转换为字符串。将两个日期字段转换为 'long' 节省了大约 50 毫秒。

通过从树适配器 (JsonSerializer/JsonDeserializer) 迁移到新的流适配器类 TypeAdaper,我能够再节省大约 10 毫秒。设置它的代码如下所示:

    private static TypeAdapter<Key<String>> keyAdapter = new TypeAdapter<Key<String>>() {
        @Override public void write(JsonWriter out, Key<String> value) throws IOException {
            out.value(value.value);
        }

        @Override public Key<String> read(JsonReader in) throws IOException {
            if (in.peek() == JsonToken.NULL) {
                in.nextNull();
                return null;
            }
            return new Key<String>(in.nextString());
        }
    };

    ...

    Gson gson = new GsonBuilder()
            .registerTypeAdapter(Key.class, keyAdapter)
            .create();

我的场景和你的场景之间的主要区别在于我使用的是我自己的伪造 Key 类。但是,如果 Key 是您手动序列化每个案例时应该出现的瓶颈。

解决问题

您最好的下一步是从Case 中删除字段,直到序列化得到改善。您的某个字段可能包含需要很长时间才能序列化的内容:可能是一个非常长的字符串,需要过度转义?一旦您将问题report a bug 隔离到 Gson 项目,我们将很乐意解决问题。除了包含重现问题的代码,您还应该包含有代表性的数据

【讨论】:

  • 如果过度转义是问题不会出现在手动序列化中吗?
  • 现在尝试其他策略
  • 好的,我尝试了 TypeAdapter 策略,但它似乎没有太大帮助。有趣的是,我的“手动”序列化代码版本不仅使用与 TypeAdapters 相同的方法序列化相同的字段(键、日期等),而且它的运行速度实际上快了 20 倍。这似乎是 gson 库本身的问题。
  • 我可以通过一个非常简单的测试来复制这种糟糕的性能。查看更新后的问题。
【解决方案2】:

【讨论】:

    猜你喜欢
    • 2015-05-22
    • 1970-01-01
    • 2012-10-28
    • 1970-01-01
    • 1970-01-01
    • 2017-12-12
    • 2015-06-14
    • 2013-05-14
    • 2021-05-24
    相关资源
    最近更新 更多