在以前的學習中,咱們在Gson全解析(上)Gson使用的基礎到分別運用了JsonSerializer
和JsonDeserializer
進行JSON和java實體類之間的相互轉化。java
在Gson全解析(中)中使用了TypeAdapter
中的read和write方法分別進行了反序列化和序列化。
咱們曾講到使用TypeAdapter
會比使用JsonSerializer
和JsonDeserializer
更加的高效,原理是怎麼樣的呢?性能提高明顯嗎?git
下面的文章給你答案。github
如下Gson性能分析,內容整理自: GSON TYPEADAPTER EXAMPLE SERIALISE LARGE OBJECTSjson
採用YourKit 做爲性能分析工具。數組
首先來看看咱們提供一個大一點的數據來論證下面一些方法的優缺點。 這裏提供類LargeData.java
,並分爲四個部分進行內存消耗的分析:ide
public class LargeData { private long[] numbers; public void create(final int length) { numbers = new long[length]; for (int i = 0; i < length; i++) { numbers[i] = i; } } public long[] getNumbers() { return numbers; } }
看看下面的JsonSerializer
:工具
package com.javacreed.examples.gson.part1; import java.lang.reflect.Type; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonPrimitive; import com.google.gson.JsonSerializationContext; import com.google.gson.JsonSerializer; public class LargeDataSerialiser implements JsonSerializer<LargeData> { @Override public JsonElement serialize(final LargeData data, final Type typeOfSrc, final JsonSerializationContext context) { final JsonArray jsonNumbers = new JsonArray(); for (final long number : data.getNumbers()) { jsonNumbers.add(new JsonPrimitive(number)); } final JsonObject jsonObject = new JsonObject(); jsonObject.add("numbers", jsonNumbers); return jsonObject; } }
上面的代碼實現了從java對象>轉化>JSON數組的序列化過程。下面的代碼實現了配置和初始化的過程,被寫入文件。這裏能夠看到的是對LargeData
初始化了10485760
個元素:源碼分析
package com.javacreed.examples.gson.part1; import java.io.File; import java.io.IOException; import java.io.PrintStream; import com.google.gson.Gson; import com.google.gson.GsonBuilder; public class Main { public static void main(final String[] args) throws IOException { // Configure GSON final GsonBuilder gsonBuilder = new GsonBuilder(); gsonBuilder.registerTypeAdapter(LargeData.class, new LargeDataSerialiser()); gsonBuilder.setPrettyPrinting(); final Gson gson = gsonBuilder.create(); final LargeData data = new LargeData(); data.create(10485760); final String json = gson.toJson(data); final File dir = new File("target/part1"); dir.mkdirs(); try (PrintStream out = new PrintStream(new File(dir, "output.json"), "UTF-8")) { out.println(json); } System.out.println("Done"); } }
這個例子實現了建立java對象而且轉化爲JSON字符串並寫入文件的整個過程。下面的圖標展現了內存的消耗狀況:性能
上面的的LargeData在這裏會消耗89MB的內存,從java對象轉化爲JSON字符串的過程將會消耗大概16s的時間而且須要超過1GB的內存。也就是說,序列化1MB的數據咱們須要大約11MB的工做空間。1:11的確實是一個不小的比列。下面的 圖片會展現整個過程的幾個階段。學習
能夠看到的是,這裏有四個方塊分別表明不一樣的階段,(可是IO 緩衝區並無在這裏獲得使用,因此以灰色進行標註。)整個過程從java對象(藍色方塊),而後由LargeDataSerialiser
類建立的JSONElement對象(紅色方塊),而後這些臨時的對象又被轉化爲JSON 字符串(綠色方塊),上面的示例代碼使用PrintStream
將內容輸出到文件中並無使用任何緩衝區。
完成了第1部分的分析,接下來下面的分析流程是同樣的:
以前的系列文章中都對Gson基礎的使用進行了很好的講解,能夠回顧一下。
TypeAdapter
相比 於上面的方法,並無使用JSONElement對象,而是直接將Java對象啊轉化爲了JSON對象。
package com.javacreed.examples.gson.part2; import java.io.IOException; import com.google.gson.TypeAdapter; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; public class LargeDataTypeAdapter extends TypeAdapter<LargeData> { @Override public LargeData read(final JsonReader in) throws IOException { throw new UnsupportedOperationException("Coming soon"); } @Override public void write(final JsonWriter out, final LargeData data) throws IOException { out.beginObject(); out.name("numbers"); out.beginArray(); for (final long number : data.getNumbers()) { out.value(number); } out.endArray(); out.endObject(); } }
一樣會須要配置,這裏主要使用的方法是gsonBuilder.registerTypeAdapter(LargeData.class, new LargeDataTypeAdapter());
:
package com.javacreed.examples.gson.part2; import java.io.File; import java.io.IOException; import java.io.PrintStream; import com.google.gson.Gson; import com.google.gson.GsonBuilder; public class Main { public static void main(final String[] args) throws IOException { // Configure GSON final GsonBuilder gsonBuilder = new GsonBuilder(); gsonBuilder.registerTypeAdapter(LargeData.class, new LargeDataTypeAdapter()); gsonBuilder.setPrettyPrinting(); final Gson gson = gsonBuilder.create(); final LargeData data = new LargeData(); data.create(10485760); final String json = gson.toJson(data); final File dir = new File("target/part2"); dir.mkdirs(); try (PrintStream out = new PrintStream(new File(dir, "output.json"), "UTF-8")) { out.println(json); } System.out.println("Done"); } }
上面的代碼完成的是從java對象 >轉化>JSON 字符串並最終寫入文件的過程。看看下面的性能分析圖表:
和最初的那個方法同樣,這裏的LargeData對象將會須要89MB的內存,從java對象轉化爲JSON字符串的過程須要消耗4s的時間,大概650MB的內存。也就是說,序列化1MB的數據,大概須要7.5MB的內存空間。相比於以前的第一種JsonSerializer方法,這裏減小了接近一半的內存消耗。一樣的,來看看這個方法的幾個過程:
這裏的序列化過程主要有兩個階段,相比於以前的JSONSerializer
的序列化過程,這裏沒有了轉化爲JSONElement的過程,也就完成了內存消耗的減小。
下面的代碼,咱們使用上面一樣的TypeAdapter,只不過咱們直接在main()方法中修改Gson的用法,以流的形式進行輸出。
package com.javacreed.examples.gson.part3; import java.io.BufferedWriter; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import com.google.gson.Gson; import com.google.gson.GsonBuilder; public class Main { public static void main(final String[] args) throws IOException { // Configure GSON final GsonBuilder gsonBuilder = new GsonBuilder(); gsonBuilder.registerTypeAdapter(LargeData.class, new LargeDataTypeAdapter()); gsonBuilder.setPrettyPrinting(); final Gson gson = gsonBuilder.create(); final LargeData data = new LargeData(); data.create(10485760); final File dir = new File("target/part3"); dir.mkdirs(); try (BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File(dir, "output.json")), "UTF-8"))) { gson.toJson(data, out); } System.out.println("Done"); } }
這個例子一樣是將java對象轉化爲JSON字符串而且輸出,也來看看下面的性能分析圖表:
能夠看到的是一樣的最初產生的數據是89MB,序列化過程將java對象轉化爲JSON字符串花了大概三秒鐘的時間,消耗大概160MB的內存。也就是說序列化1MB的數據咱們須要大概2MB的內存空間。相比於以前的兩種方法,有了很大的改進。
這個方法一樣的是使用了兩個階段。不過在上面一個示例中的綠色方塊部分在這裏沒有使用,這裏直接完成了java對象到IO 緩衝區的轉化並寫入文件。
雖然這裏並非Gson的關係,可是咱們使用Gson的方法極大的減小了內存消耗,因此說在使用開源庫的時候,可以正確高效的使用API也顯得尤其重要。
一樣的使用第一個例子中的JsonSerializer,這裏的配置須要注意的是gsonBuilder.registerTypeAdapter(LargeData.class, new LargeDataSerialiser());
package com.javacreed.examples.gson.part4; import java.io.BufferedWriter; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import com.google.gson.Gson; import com.google.gson.GsonBuilder; public class Main { public static void main(final String[] args) throws IOException { // Configure GSON final GsonBuilder gsonBuilder = new GsonBuilder(); gsonBuilder.registerTypeAdapter(LargeData.class, new LargeDataSerialiser()); gsonBuilder.setPrettyPrinting(); final Gson gson = gsonBuilder.create(); final LargeData data = new LargeData(); data.create(10485760); final File dir = new File("target/part4"); dir.mkdirs(); try (BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File(dir, "output.json")), "UTF-8"))) { gson.toJson(data, out); } System.out.println("Done"); } }
通過前面的分析,咱們這裏也能夠這道這裏主要分爲三個階段,下面提供性能分析圖和JSONSerializer的階段流程圖:
這裏能夠看到三個階段完成的工做消耗了11s的時間,730MB的內存空間。也就是說1:8的比例。能夠相比上面的例子,知道這裏使用JSONSerializer產生了JSONElement對象消耗了不少的內存。
在上面的分析過程當中,咱們採用了GSON的兩種不一樣的方然完成了序列化一個大數據的過程,而且比較了不一樣的方法之間的差別。上面的第三種方法(TypeAdapter的流式處理)被論證爲最合適的,消耗最少內存的一種方法。
Gson主要分紅兩部分,一個就是數據拆解,一個是數據封裝。
https://sites.google.com/site/gson/gson-user-guide
翻譯原文,根據原文作出了較大改動。
1 SIMPLE GSON EXAMPLE
2 GSON DESERIALISER EXAMPLE
3 GSON ANNOTATIONS EXAMPLE
4 GSON SERIALISER EXAMPLE
5 GSON TYPEADAPTER EXAMPLE
6 GSON TYPEADAPTER EXAMPLE SERIALISE LARGE OBJECTS
另附: 你真的會用Gson嗎?Gson使用指南(一)系列文章
本篇文章是本系列博客的第三篇文章。將從源碼角度以及Gson的深刻用法講起,一塊兒來學習吧。
本系列文章是基於Gson官方使用指導(Gson User Guide)以及Gson解析的優秀外文(來自http://www.javacreed.com/ )作出的一個翻譯和概括。
博客原連接:
Gson全解析(上)-Gson基礎
Gson全解析(中)-TypeAdapter的使用
Gson全解析(下)-Gson性能分析
from: http://www.jianshu.com/p/8cc857583ff4