JAXB性能优化

 

前言:
  之前在查阅jaxb相关资料的同时, 也看到了一些关于性能优化的点. 主要集中于对象和xml互转的过程中, 确实有些实实在在需要注意的点. 这边浅谈jaxb性能优化的一个思路.

 

案列:
  先来构造一个简单的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
@XmlRootElement(name="txn")
@XmlAccessorType(XmlAccessType.FIELD)
public static class TNode {
 
    @XmlElement(name="key", required = true)
    private String key;
 
    @XmlElement(name="value", required = true)
    private String value;
 
}

  注: 这个基本的映射对象类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static <T> String writeAsString(T t) {
    try {
        JAXBContext jc = JAXBContext.newInstance(t.getClass());
        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
 
        StringWriter writer = new StringWriter();
        marshaller.marshal(t, writer);
        return writer.toString();
    } catch (JAXBException ex) {
        ex.printStackTrace();
    }
    return null;
}

  注: 这是最经典的java对象转化为xml的代码片段.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Test
public void testPref() {
 
    TNode node = new TNode("key", "value");
 
    // *) 迭代重复的次数
    int numIter = 1000;
    long startTime = System.currentTimeMillis();
    for ( int i = 0; i < numIter; i++ ) {
        writeAsString(node);
    }
    long endTime = System.currentTimeMillis();
 
    System.out.println(String.format("iter num: %d, consume: %dms, avg: %.2fms",
            numIter, (endTime - startTime), (endTime - startTime) * 1.0 / numIter));
 
}

  注: 这是实际执行的性能评估代码, 注意这边的迭代次数.

  测试一下在迭代100/1000/10000次的, 总耗时及平均耗时.

iter num: 100, consume: 2001ms, avg: 20.01ms
iter num: 1000, consume: 11020ms, avg: 11.02ms
iter num: 10000, consume: 108290ms, avg: 10.83ms

  大致维持在10ms, 这算一个比较酸涩的结果, 如果映射是个复杂的对象, 其耗时会成倍的增加, 对于追求高并发低延时的互联网应用而言, 略显尴尬, redis平均为1ms, mysql/mongo基本维持在10ms范围内. 因此jaxb虽然非常好用, 但是性能需要优化.

 

瓶颈定位&优化:
  经过测试, 基本聚焦在JAXBContext.newInstance这个方法中. 其生成的JAXBContext实例代价高, 但其是无状态(线程安全), 我们可以想到的一个优化措施是缓存.
  对转换代码做下小改动:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
private static ConcurrentHashMap<Class, JAXBContext> jaxbContMap
            = new ConcurrentHashMap<Class, JAXBContext>();
 
public static <T> String writeAsString(T t) {
    try {
        JAXBContext jc = jaxbContMap.get(t.getClass());
        if ( jc == null ) {
            synchronized (t.getClass()) {
                jc = JAXBContext.newInstance(t.getClass());
                jaxbContMap.put(t.getClass(), jc);
            }
        }
        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
 
        StringWriter writer = new StringWriter();
        marshaller.marshal(t, writer);
        return writer.toString();
    } catch (JAXBException ex) {
        ex.printStackTrace();
    }
    return null;
}

  注: 引入ConcurrentHashMap来缓存类类型到具体的JAXBContext实例.
  再测试100/1000/10000调用次数的平均时耗, 结果如下:

iter num: 100, consume: 295ms, avg: 2.95ms
iter num: 1000, consume: 1325ms, avg: 1.33ms
iter num: 10000, consume: 5688ms, avg: 0.57ms

  结果非常令人欣喜, 耗时能够维持在1ms以内, 优化提升效果很明显.

 

总结:
  总的来说, 这也是jaxb在使用过程的一个坑, 如果线上应用, 切记改为下面的方式优化, ^_^, 本文没有深入研究为何JAXBContext.newInstance代价这么高昂, 只是得出了一个小结论, 权当笔记.

 

posted on   mumuxinfei  阅读(1150)  评论(0编辑  收藏  举报

编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
历史上的今天:
2014-05-10 ODBC的基础架构

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

统计

点击右上角即可分享
微信分享提示