-
Notifications
You must be signed in to change notification settings - Fork 114
Presentation: Jackson Performance
Although Jackson JSON Processor is fast right out-of-the-box, with default settings and common usage patterns, there are ways to make it process things even faster. This presentation looks at couple of things you can use that can make a big difference in performance, for cases where every last drop of CPU power is needed.
(note: this section is inspired by Jackson Performance: best practices Uncyclo page at FasterXML Jackson Uncyclo
There is a small number of basic ground rules to follow, to ensure that Jackson processes things at optimal level. These are things that you should "do anyway", even if you do not have actual performance problems: think of them as an interpretation of the "Boy Scout Rule" ("Always leave the campground cleaner than you found it"). Note that guidelines are shown in loosely decreasing order of importance.
- Reuse heavy-weight objects:
ObjectMapper
(data-binding) and `JsonFactory (streaming API)
- To a lesser degree, you may also want to reuse
ObjectReader
andObjectWriter
instances -- this is just some icing on the cake, but they are fully thread-safe and reusable
- Close things that need to be closed:
JsonParser
,JsonGenerator
- This helps reuse underlying things such as symbol tables, reusable input/output buffers
- Nothing to close for
ObjectMapper
- Use "unrefined" (least processed) forms of input: i.e. do not try decorating input sources and output targets:
- Input:
byte[]
is better if you have it;InputStream
next best;Reader
then -- and in every case, do NOT try reading input into String! - Output:
OutputStream
is best;Writer
second best; callingwriteValueAsString()
is the least efficient (why construct intermediate String?) - Rationale: Jackson is very good at finding the most efficient (sometimes zero-copy) way to consume/produce JSON encoded data -- let it do its magic
- If you need to re-process, replay, don't re-parse
- Sometimes you need to process things in multiple phases; for example, you may need to parser part of JSON to figure out further processing or data-binding rules, and/or modify intermediate presentation for further processing
- Instead of writing out intermediate forms back as JSON (which will incur both JSON writing and reading overhead), it is better to use a more efficient intermediate form
- The most efficient intermediate form is
TokenBuffer
(flat sequence of JSON Tokens); followed by JSON Tree model (JsonNode
) - May also want to use
ObjetMapper.convertValue()
, to convert between Object types
- Use
ObjectReader
methodreadValues()
for reading sequences of same POJO type
- Functionally equivalent to calling
readValue()
multiple times, but both more convenient AND (slightly) more efficient
- Prefer 'ObjectReader'/'ObjectWriter' over 'ObjectMapper'
-
ObjectReader
andObjectWriter
are safer to use -- they are fully immutable and freely shareable between threads -- but they can also be bit more efficient, since they can avoid some of the lookups thatObjectMapper
has to do
Once you have reviewed "the basics" discussed above, you may want to consider other tasks specifically aimed at further improving performance.
There are two main criteria that differentiate approaches listed below:
- Ease -- how much work is involved in making the change
- Compatibility -- is the resulting system interoperable with "Plain Old JSON" usage?
The big benefit of Jackson Databind API is the ease of use: with just a line or two of code you can convert between POJOs and JSON. But this convenience is not completely free: there is overhead involved in some of the automated processing, such as that of handling POJO property values using Java Reflection API (compared to explicit calls to getters and setters).
So one straight-forward (if laborious) possibility is to rewrite data conversion to use Jackson Streaming API.
With Streaming API one has to construct JsonParser
s and JsonGenerator
s, and use low-level calls to read and write JSON as tokens.
If you explicitly rewrite all the conversions to use Streaming API instead of data binding, you may be able to increase through-put by 30-40%; and this without any changes to actual JSON produced. But writing and maintaining the low-level code takes time and effort, so whether you want to do this depends on how much you want to invest in getting moderate speedup.
One possible trade-off is that of only rewriting parts of the process; specifically, optimizing most commonly used conversions: these are usually leaf-level classes (classes that have only primitive or String -valued properties). You can achieve this by only writing JsonSerializer
s and JsonDeserializer
s for small number of types; Jackson can happily use both its own default POJO serializers, deserializers, and custom overrides for specific types.