public String getCarString(Car car){ ObjectMapper objectMapper = new ObjectMapper(); String str = objectMapper.writeValueAsString(car); return str; }
这种代码就在CV工程师手中遍地开了花。神奇。
你要说它有问题,它确实能正确的执行。你要说它没问题,在追求性能的同学眼里,这肯定是一段十恶不赦的代码。一般的工具类,都是单例的,同时是线程安全的。ObjectMapper也不例外,它也是线程安全的,你可以并发的执行它,不会产生任何问题。这段代码,ObjectMapper在每次方法调用的时候,都会生成一个。那它除了造成一定的年轻代内存浪费之外,在执行时间上有没有什么硬伤呢?
有一次,xjjdog隐晦的指出某段被频繁调用的代码问题,被小伙伴怒吼着拿出证据。证据?这得搬出Java中的基准测试工具JMH,才能一探究竟。JMH(the Java Microbenchmark Harness) 就是这样一个能够做基准测试的工具。如果你通过我们一系列的工具,定位到了热点代码,要测试它的性能数据,评估改善情况,就可以交给JMH。它的测量精度非常高,最高可达到纳秒的级别。
@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,每秒可以执行200万次JSON解析;如果全局使用一个ObjectMapper,则每秒可以执行2000多万次,速度足足快了10倍。如果使用ThreadLocal的方式,每个线程给它分配一个解析器,则性能会有少许上升,但也没有达到非常夸张的地步。
所以在项目中写代码的时候,我们只需要保证有一个全局的ObjectMapper就可以了。
当然,由于ObjectMapper有很多的特性需要配置,你可能会为不同的应用场景分配一个单独使用的ObjectMapper。总之,它的数量不需要太多,因为它是线程安全的。