前言

在之前的学习中,我们在Gson全解析(上)Gson使用的基础到分别运用了JsonSerializerJsonDeserializer进行JSON和java实体类之间的相互转化。

Gson全解析(中)中使用了TypeAdapter中的read和write方法分别进行了反序列化和序列化。
我们曾讲到使用TypeAdapter会比使用JsonSerializerJsonDeserializer更加的高效,原理是怎么样的呢?性能提升明显吗?

下面的文章给你答案。

Gson性能分析

以下Gson性能分析,内容整理自: GSON TYPEADAPTER EXAMPLE SERIALISE LARGE OBJECTS

采用YourKit作为性能分析工具。

首先来看看我们提供一个大一点的数据来论证下面一些方法的优缺点。 这里提供类LargeData.java,并分为四个部分进行内存消耗的分析:

  1. public class LargeData {
  2. private long[] numbers;
  3. public void create(final int length) {
  4. numbers = new long[length];
  5. for (int i = 0; i < length; i++) {
  6. numbers[i] = i;
  7. }
  8. }
  9. public long[] getNumbers() {
  10. return numbers;
  11. }
  12. }

第1部分 JsonSerializer的直接使用

看看下面的JsonSerializer:

  1. package com.javacreed.examples.gson.part1;
  2. import java.lang.reflect.Type;
  3. import com.google.gson.JsonArray;
  4. import com.google.gson.JsonElement;
  5. import com.google.gson.JsonObject;
  6. import com.google.gson.JsonPrimitive;
  7. import com.google.gson.JsonSerializationContext;
  8. import com.google.gson.JsonSerializer;
  9. public class LargeDataSerialiser implements JsonSerializer<LargeData> {
  10. @Override
  11. public JsonElement serialize(final LargeData data, final Type typeOfSrc, final JsonSerializationContext context) {
  12. final JsonArray jsonNumbers = new JsonArray();
  13. for (final long number : data.getNumbers()) {
  14. jsonNumbers.add(new JsonPrimitive(number));
  15. }
  16. final JsonObject jsonObject = new JsonObject();
  17. jsonObject.add("numbers", jsonNumbers);
  18. return jsonObject;
  19. }
  20. }

上面的代码实现了从java对象>转化>JSON数组的序列化过程。下面的代码实现了配置和初始化的过程,被写入文件。这里可以看到的是对LargeData初始化了10485760个元素:

  1. package com.javacreed.examples.gson.part1;
  2. import java.io.File;
  3. import java.io.IOException;
  4. import java.io.PrintStream;
  5. import com.google.gson.Gson;
  6. import com.google.gson.GsonBuilder;
  7. public class Main {
  8. public static void main(final String[] args) throws IOException {
  9. // Configure GSON
  10. final GsonBuilder gsonBuilder = new GsonBuilder();
  11. gsonBuilder.registerTypeAdapter(LargeData.class, new LargeDataSerialiser());
  12. gsonBuilder.setPrettyPrinting();
  13. final Gson gson = gsonBuilder.create();
  14. final LargeData data = new LargeData();
  15. data.create(10485760);
  16. final String json = gson.toJson(data);
  17. final File dir = new File("target/part1");
  18. dir.mkdirs();
  19. try (PrintStream out = new PrintStream(new File(dir, "output.json"), "UTF-8")) {
  20. out.println(json);
  21. }
  22. System.out.println("Done");
  23. }
  24. }

这个例子实现了创建java对象并且转化为JSON字符串并写入文件的整个过程。下面的图标展示了内存的消耗情况:

JsonSerializer-Profile

上面的的LargeData在这里会消耗89MB的内存,从java对象转化为JSON字符串的过程将会消耗大概16s的时间并且需要超过1GB的内存。也就是说,序列化1MB的数据我们需要大约11MB的工作空间。1:11的确实是一个不小的比列。下面的 图片会展示整个过程的几个阶段。

JsonSerializer-Stages

可以看到的是,这里有四个方块分别代表不同的阶段,(但是IO 缓冲区并没有在这里得到使用,所以以灰色进行标注。)整个过程从java对象(蓝色方块),然后由LargeDataSerialiser类创建的JSONElement对象(红色方块),然后这些临时的对象又被转化为JSON 字符串(绿色方块),上面的示例代码使用PrintStream将内容输出到文件中并没有使用任何缓冲区。

完成了第1部分的分析,接下来下面的分析流程是一样的:

第2 部分 TypeAdapter的直接使用

之前的系列文章中都对Gson基础的使用进行了很好的讲解,可以回顾一下。

TypeAdapter相比 于上面的方法,并没有使用JSONElement对象,而是直接将Java对象啊转化为了JSON对象。

  1. package com.javacreed.examples.gson.part2;
  2. import java.io.IOException;
  3. import com.google.gson.TypeAdapter;
  4. import com.google.gson.stream.JsonReader;
  5. import com.google.gson.stream.JsonWriter;
  6. public class LargeDataTypeAdapter extends TypeAdapter<LargeData> {
  7. @Override
  8. public LargeData read(final JsonReader in) throws IOException {
  9. throw new UnsupportedOperationException("Coming soon");
  10. }
  11. @Override
  12. public void write(final JsonWriter out, final LargeData data) throws IOException {
  13. out.beginObject();
  14. out.name("numbers");
  15. out.beginArray();
  16. for (final long number : data.getNumbers()) {
  17. out.value(number);
  18. }
  19. out.endArray();
  20. out.endObject();
  21. }
  22. }

同样会需要配置,这里主要使用的方法是
gsonBuilder.registerTypeAdapter(LargeData.class, new LargeDataTypeAdapter());

  1. package com.javacreed.examples.gson.part2;
  2. import java.io.File;
  3. import java.io.IOException;
  4. import java.io.PrintStream;
  5. import com.google.gson.Gson;
  6. import com.google.gson.GsonBuilder;
  7. public class Main {
  8. public static void main(final String[] args) throws IOException {
  9. // Configure GSON
  10. final GsonBuilder gsonBuilder = new GsonBuilder();
  11. gsonBuilder.registerTypeAdapter(LargeData.class, new LargeDataTypeAdapter());
  12. gsonBuilder.setPrettyPrinting();
  13. final Gson gson = gsonBuilder.create();
  14. final LargeData data = new LargeData();
  15. data.create(10485760);
  16. final String json = gson.toJson(data);
  17. final File dir = new File("target/part2");
  18. dir.mkdirs();
  19. try (PrintStream out = new PrintStream(new File(dir, "output.json"), "UTF-8")) {
  20. out.println(json);
  21. }
  22. System.out.println("Done");
  23. }
  24. }

上面的代码完成的是从java对象 >转化>JSON 字符串并最终写入文件的过程。看看下面的性能分析图表:

TypeAdapter-Profile

和最初的那个方法一样,这里的LargeData对象将会需要89MB的内存,从java对象转化为JSON字符串的过程需要消耗4s的时间,大概650MB的内存。也就是说,序列化1MB的数据,大概需要7.5MB的内存空间。相比于之前的第一种JsonSerializer方法,这里减少了接近一半的内存消耗。同样的,来看看这个方法的几个过程:

TypeAdapter-Stages

这里的序列化过程主要有两个阶段,相比于之前的JSONSerializer的序列化过程,这里没有了转化为JSONElement的过程,也就完成了内存消耗的减少。

第3部分 TypeAdapter的流式处理

下面的代码,我们使用上面同样的TypeAdapter,只不过我们直接在main()方法中修改Gson的用法,以流的形式进行输出。

  1. package com.javacreed.examples.gson.part3;
  2. import java.io.BufferedWriter;
  3. import java.io.File;
  4. import java.io.FileOutputStream;
  5. import java.io.IOException;
  6. import java.io.OutputStreamWriter;
  7. import com.google.gson.Gson;
  8. import com.google.gson.GsonBuilder;
  9. public class Main {
  10. public static void main(final String[] args) throws IOException {
  11. // Configure GSON
  12. final GsonBuilder gsonBuilder = new GsonBuilder();
  13. gsonBuilder.registerTypeAdapter(LargeData.class, new LargeDataTypeAdapter());
  14. gsonBuilder.setPrettyPrinting();
  15. final Gson gson = gsonBuilder.create();
  16. final LargeData data = new LargeData();
  17. data.create(10485760);
  18. final File dir = new File("target/part3");
  19. dir.mkdirs();
  20. try (BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File(dir,
  21. "output.json")), "UTF-8"))) {
  22. gson.toJson(data, out);
  23. }
  24. System.out.println("Done");
  25. }
  26. }

这个例子同样是将java对象转化为JSON字符串并且输出,也来看看下面的性能分析图表:

TypeAdapter-with-Stream-Profile

可以看到的是同样的最初产生的数据是89MB,序列化过程将java对象转化为JSON字符串花了大概三秒钟的时间,消耗大概160MB的内存。也就是说序列化1MB的数据我们需要大概2MB的内存空间。相比于之前的两种方法,有了很大的改进。

TypeAdapter-with-Stream-Stages

这个方法同样的是使用了两个阶段。不过在上面一个示例中的绿色方块部分在这里没有使用,这里直接完成了java对象到IO 缓冲区的转化并写入文件。

虽然这里并不是Gson的关系,但是我们使用Gson的方法极大的减少了内存消耗,所以说在使用开源库的时候,能够正确高效的使用API也显得尤为重要。

第4部分 JsonSerializer 的流式处理

同样的使用第一个例子中的JsonSerializer,这里的配置需要注意的是gsonBuilder.registerTypeAdapter(LargeData.class, new LargeDataSerialiser());

  1. package com.javacreed.examples.gson.part4;
  2. import java.io.BufferedWriter;
  3. import java.io.File;
  4. import java.io.FileOutputStream;
  5. import java.io.IOException;
  6. import java.io.OutputStreamWriter;
  7. import com.google.gson.Gson;
  8. import com.google.gson.GsonBuilder;
  9. public class Main {
  10. public static void main(final String[] args) throws IOException {
  11. // Configure GSON
  12. final GsonBuilder gsonBuilder = new GsonBuilder();
  13. gsonBuilder.registerTypeAdapter(LargeData.class, new LargeDataSerialiser());
  14. gsonBuilder.setPrettyPrinting();
  15. final Gson gson = gsonBuilder.create();
  16. final LargeData data = new LargeData();
  17. data.create(10485760);
  18. final File dir = new File("target/part4");
  19. dir.mkdirs();
  20. try (BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File(dir,
  21. "output.json")), "UTF-8"))) {
  22. gson.toJson(data, out);
  23. }
  24. System.out.println("Done");
  25. }
  26. }

经过前面的分析,我们这里也可以这道这里主要分为三个阶段,下面提供性能分析图和JSONSerializer的阶段流程图:

JsonSerializer-with-Stream-Profile

JsonSerializer-with-Stream-Stages

这里可以看到三个阶段完成的工作消耗了11s的时间,730MB的内存空间。也就是说1:8的比例。可以相比上面的例子,知道这里使用JSONSerializer产生了JSONElement对象消耗了很多的内存。

结论

在上面的分析过程中,我们采用了GSON的两种不同的方然完成了序列化一个大数据的过程,并且比较了不同的方法之间的差异。上面的第三种方法(TypeAdapter的流式处理)被论证为最合适的,消耗最少内存的一种方法。

Gson主要分成两部分,一个就是数据拆解,一个是数据封装。

参考链接

Gson的源码分析(1)-开篇

https://sites.google.com/site/gson/gson-user-guide

翻译原文,根据原文做出了较大改动。
SIMPLE GSON EXAMPLE
GSON DESERIALISER EXAMPLE
GSON ANNOTATIONS EXAMPLE
GSON SERIALISER EXAMPLE
GSON TYPEADAPTER EXAMPLE
GSON TYPEADAPTER EXAMPLE SERIALISE LARGE OBJECTS

另附: 你真的会用Gson吗?Gson使用指南(一)系列文章

gson github地址google/gson

本篇文章是本系列博客的第三篇文章。将从源码角度以及Gson的深入用法讲起,一起来学习吧。
本系列文章是基于Gson官方使用指导(Gson User Guide)以及Gson解析的优秀外文(来自http://www.javacreed.com/ )做出的一个翻译和归纳。

博客原链接:
Gson全解析(上)-Gson基础
Gson全解析(中)-TypeAdapter的使用
Gson全解析(下)-Gson性能分析

from: http://www.jianshu.com/p/8cc857583ff4

Gson全解析(下)-Gson性能分析的更多相关文章

  1. Gson全解析(中)-TypeAdapter的使用

    TypeAdapter介绍 前面的Gson全解析(上)中我们理解并分别运用了JsonSerializer和JsonDeserializer进行JSON和java实体类之间的相互转化.这里利用TypeA ...

  2. Gson全解析(上)-Gson基础

    前言 最近在研究Retrofit中使用的Gson的时候,发现对Gson的一些深层次的概念和使用比较模糊,所以这里做一个知识点的归纳整理. Gson(又称Google Gson)是Google公司发布的 ...

  3. (转)超全整理!Linux性能分析工具汇总合集

    超全整理!Linux性能分析工具汇总合集 原文:http://rdc.hundsun.com/portal/article/731.html 出于对Linux操作系统的兴趣,以及对底层知识的强烈欲望, ...

  4. 超全整理!Linux性能分析工具汇总合集

    转自:http://rdc.hundsun.com/portal/article/731.html?ref=myread 出于对Linux操作系统的兴趣,以及对底层知识的强烈欲望,因此整理了这篇文章. ...

  5. 硬核测试:Pulsar 与 Kafka 在金融场景下的性能分析

    背景 Apache Pulsar 是下一代分布式消息流平台,采用计算存储分层架构,具备多租户.高一致.高性能.百万 topic.数据平滑迁移等诸多优势.越来越多的企业正在使用 Pulsar 或者尝试将 ...

  6. Linux性能分析的前60000毫秒【转】

    Linux性能分析的前60000毫秒 为了解决性能问题,你登入了一台Linux服务器,在最开始的一分钟内需要查看什么? 在Netflix我们有一个庞大的EC2 Linux集群,还有非常多的性能分析工具 ...

  7. Chrome Performance性能分析面板使用

    最近做的项目都是内嵌egret游戏,想在移动端监测下它的性能,于是就开始了对Performance的探索: 一.使用 打开控制台,一顿操作: 网络选择Fast 3G,模拟手机普通3G环境,虽然现在大家 ...

  8. 浅谈Unity的渲染优化(1): 性能分析和瓶颈判断(上篇)

    http://www.taidous.com/article-667-1.html 前言 首先,这个系列文章做个大致的介绍,题目"浅谈Unity",因为公司和国内大部分3D手游开发 ...

  9. 性能分析 函数粒度 函数里的一条语句 汇编 反编译 机器指令 %rbx,%rbp

    在Linux下做性能分析3:perf - 知乎 https://zhuanlan.zhihu.com/p/22194920 Linux Perf 性能分析工具及火焰图浅析 - 知乎 https://z ...

随机推荐

  1. 【LOJ】#2320. 「清华集训 2017」生成树计数

    题解 我,理解题解,用了一天 我,卡常数,又用了一天 到了最后,我才发现,我有个加法取模,写的是while(c >= MOD) c -= MOD 我把while改成if,时间,少了 六倍. 六倍 ...

  2. pyqt5猜数小程序

    程序界面用qt设计师制作,并用pyuic5命令转换成form.py文件 #-*- coding:utf-8 -*- from PyQt5.QtWidgets import QApplication,Q ...

  3. LoadRunner中log的使用总结

    LoadRunner中log的使用总结 1.log的设置方式. 在 runtime setting中可以设置log的生成方式: 默认的log方式: Enable logging选中,log optio ...

  4. 认识loadrunner及相关性能参数

    认识loadrunner及相关性能参数 LoadRunner,是一种预测系统行为和性能的负载测试工具.通过以模拟上千万用户实施并发负载及实时性能监测的方式来确认和查找问题,LoadRunner能够对整 ...

  5. Java学习笔记之:Struts2.0 环境搭建

    一.介绍 Struts2是一个基于MVC设计模式的Web应用框架,它本质上相当于一个servlet,在MVC设计模式中,Struts2作为控制器(Controller)来建立模型与视图的数据交互. 二 ...

  6. logstash部署及基本语法(二)

    一.logstash介绍 Logstash是一个开源的数据收集引擎,可以水平伸缩,而且logstash是整个ELK当中拥有最多插件的一个组件,其可以接收来自不同源的数据并统一输入到指定的且可以是不同目 ...

  7. jump game(贪心算法)

    Given an array of non-negative integers, you are initially positioned at the first index of the arra ...

  8. [ 转载 ] Java基础13--equals方法

    一.equals方法介绍 1.1.通过下面的例子掌握equals的用法 1 package cn.galc.test; 2 3 public class TestEquals { 4 public s ...

  9. WSGI剖析

    在一个 HTTP 请求到达服务器时, 服务器接收并调用 web 应用程序解析请求, 产生响应数据并返回给服务器. 这里涉及了两个方面的东西: 服务器(server)和应用程序(application) ...

  10. Go Web编程 第四章--处理请求

    请求和响应 Request结构 URL字段 Header字段 Body字段 Form, PostForm, MultipartForm字段 在处理器函数中,我们可以通过Request获取各个字段的详细 ...