使用ObjectMapper解析json不用一直new了
前言
自從國產(chǎn)之光fastjson頻頻暴雷,jackson json的使用是越來越廣泛了。尤其是spring家族把它搞成了默認的JSON處理包,jackson的使用數(shù)量更是呈爆炸式發(fā)展。
很多同學發(fā)現(xiàn),jackson并沒有類似fastjson的JSON.parseObjec這樣的,確實看起來很快的方法。要想解析json,你不得不new一個ObjectMapper,來處理真正的解析動作。
就像下面這樣。
public String getCarString(Car car){ ObjectMapper objectMapper = new ObjectMapper(); String str = objectMapper.writeValueAsString(car); return str; }
這種代碼就在CV工程師手中遍地開了花。
神奇。
這代碼有問題么?
你要說它有問題,它確實能正確的執(zhí)行。你要說它沒問題,在追求性能的同學眼里,這肯定是一段十惡不赦的代碼。
一般的工具類,都是單例的,同時是線程安全的。ObjectMapper也不例外,它也是線程安全的,你可以并發(fā)的執(zhí)行它,不會產(chǎn)生任何問題。
這段代碼,ObjectMapper在每次方法調用的時候,都會生成一個。那它除了造成一定的年輕代內存浪費之外,在執(zhí)行時間上有沒有什么硬傷呢?
new和不new,真的區(qū)別有那么大么?
有一次,xjjdog隱晦的指出某段被頻繁調用的代碼問題,被小伙伴怒吼著拿出證據(jù)。
證據(jù)?這得搬出Java中的基準測試工具JMH,才能一探究竟。
JMH(the Java Microbenchmark Harness) 就是這樣一個能夠做基準測試的工具。如果你通過我們一系列的工具,定位到了熱點代碼,要測試它的性能數(shù)據(jù),評估改善情況,就可以交給JMH。它的測量精度非常高,最高可達到納秒的級別。
JMH是一個jar包,它和單元測試框架JUnit非常的像,可以通過注解進行一些基礎配置。這部分配置有很多是可以通過main方法的OptionsBuilder進行設置的。
上圖是一個典型的JMH程序執(zhí)行的內容。通過開啟多個進程,多個線程,首先執(zhí)行預熱,然后執(zhí)行迭代,最后匯總所有的測試數(shù)據(jù)進行分析。在執(zhí)行前后,還可以根據(jù)粒度處理一些前置和后置操作。
JMH測試結果
為了測試上面的場景,我們創(chuàng)造了下面的基準測試類。分為三個測試場景:
- 直接在方法里new ObjectMapper
- 在全局共享一個ObjectMapper
使用ThreadLocal,每個線程一個ObjectMapper
這樣的測試屬于cpu密集型的。我的cpu有10核,直接就分配了10個線程的并發(fā),cpu在測試期間跑的滿滿的。
@BenchmarkMode({Mode.Throughput}) @OutputTimeUnit(TimeUnit.SECONDS) @State(Scope.Thread) @Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) @Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) @Fork(1) @Threads(10) public class ObjectMapperTest { String json = "{ \"color\" : \"Black\", \"type\" : \"BMW\" }"; @State(Scope.Benchmark) public static class BenchmarkState { ObjectMapper GLOBAL_MAP = new ObjectMapper(); ThreadLocal<ObjectMapper> GLOBAL_MAP_THREAD = new ThreadLocal<>(); } @Benchmark public Map globalTest(BenchmarkState state) throws Exception{ Map map = state.GLOBAL_MAP.readValue(json, Map.class); return map; } @Benchmark public Map globalTestThreadLocal(BenchmarkState state) throws Exception{ if(null == state.GLOBAL_MAP_THREAD.get()){ state.GLOBAL_MAP_THREAD.set(new ObjectMapper()); } Map map = state.GLOBAL_MAP_THREAD.get().readValue(json, Map.class); return map; } @Benchmark public Map localTest() throws Exception{ ObjectMapper objectMapper = new ObjectMapper(); Map map = objectMapper.readValue(json, Map.class); return map; } public static void main(String[] args) throws Exception { Options opts = new OptionsBuilder() .include(ObjectMapperTest.class.getSimpleName()) .resultFormat(ResultFormatType.CSV) .build(); new Runner(opts).run(); } }
測試結果如下。
Benchmark Mode Cnt Score Error Units
ObjectMapperTest.globalTest thrpt 5 25125094.559 ± 1754308.010 ops/s
ObjectMapperTest.globalTestThreadLocal thrpt 5 31780573.549 ± 7779240.155 ops/s
ObjectMapperTest.localTest thrpt 5 2131394.345 ± 216974.682 ops/s
從測試結果可以看出,如果我們每次調用都new一個ObjectMapper,每秒可以執(zhí)行200萬次JSON解析;如果全局使用一個ObjectMapper,則每秒可以執(zhí)行2000多萬次,速度足足快了10倍。
如果使用ThreadLocal的方式,每個線程給它分配一個解析器,則性能會有少許上升,但也沒有達到非??鋸埖牡夭?。
所以在項目中寫代碼的時候,我們只需要保證有一個全局的ObjectMapper就可以了。
當然,由于ObjectMapper有很多的特性需要配置,你可能會為不同的應用場景分配一個單獨使用的ObjectMapper。總之,它的數(shù)量不需要太多,因為它是線程安全的。
End
所以結論就比較清晰了,我們只需要在整個項目里使用一個ObjectMapper就可以了,沒必要傻不拉幾的每次都new一個,畢竟性能差了10倍。如果你的JSON有很多自定義的配置,使用全局的變量更能凸顯它的優(yōu)勢。
不要覺得這樣做沒有必要,保持良好的編碼習慣永遠是好的。高性能的代碼都是點點滴滴積累起來的。不積跬步,無以至千里。不積小流,無以成江海,說的就是這個道理。
以上就是使用ObjectMapper解析json不用一直new了的詳細內容,更多關于ObjectMapper解析json的資料請關注腳本之家其它相關文章!
相關文章
Spring Cloud Feign 自定義配置(重試、攔截與錯誤碼處理) 代碼實踐
這篇文章主要介紹了Spring Cloud Feign 自定義配置(重試、攔截與錯誤碼處理) 實踐,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-08-08Java從數(shù)據(jù)庫中讀取Blob對象圖片并顯示的方法
這篇文章主要介紹了Java從數(shù)據(jù)庫中讀取Blob對象圖片并顯示的方法,實例分析了Java讀取數(shù)據(jù)庫中Blob對象圖片的技巧與操作方法,需要的朋友可以參考下2015-02-02Java SSM框架(Spring+SpringMVC+MyBatis)搭建過程
最近一段時間搭建了ssm環(huán)境,并測試了幾個小項目,下面小編通過圖文并茂的形式給大家分享Java SSM框架(Spring+SpringMVC+MyBatis)搭建過程,需要的朋友參考下吧2017-11-11idea2020安裝MybatisCodeHelper插件的圖文教程
這篇文章主要介紹了idea2020安裝MybatisCodeHelper插件的方法,本文通過圖文并茂的形式給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-09-09