Should I declare Jackson's ObjectMapper as a static field?
Asked Answered
G

6

471

The Jackson library's ObjectMapper class seems to be thread safe.

Does this mean that I should declare my ObjectMapper as a static field like this

class Me {
    private static final ObjectMapper mapper = new ObjectMapper();
}

instead of as an instance-level field like this?

class Me {
    private final ObjectMapper mapper = new ObjectMapper();
}
Grenoble answered 11/10, 2010 at 16:8 Comment(0)
S
629

Yes, that is safe and recommended.

The only caveat from the page you referred is that you can't be modifying configuration of the mapper once it is shared; but you are not changing configuration so that is fine. If you did need to change configuration, you would do that from the static block and it would be fine as well.

EDIT: (2013/10)

With 2.0 and above, above can be augmented by noting that there is an even better way: use ObjectWriter and ObjectReader objects, which can be constructed by ObjectMapper. They are fully immutable, thread-safe, meaning that it is not even theoretically possible to cause thread-safety issues (which can occur with ObjectMapper if code tries to re-configure instance).

Shinbone answered 11/10, 2010 at 20:49 Comment(23)
Do you have any reference source?Grenoble
@StaxMan: I am a bit concerned if ObjectMapper is still thread-safe after ObjectMapper#setDateFormat() is called. It is known that SimpleDateFormat is not thread safe, thus ObjectMapper won't be unless it clones e.g. SerializationConfig before each writeValue() (I doubt). Could you debunk my fear?Blotchy
DateFormat is indeed cloned under the hood. Good suspicion there, but you are covered. :)Shinbone
Thanks, I have found the code that clones in DateBasedDeserializer / DateTimeSerializerBase.Blotchy
I've faced strange behaviors during unit/integration tests of a large enterprise application. When putting ObjectMapper as static final class attribute I started facing PermGen issues. Would anyone care to explain probable causes? I was using jackson-databind version 2.4.1.Codel
@AlejoCeballos my guess would be that you might be parsing documents with "random" (arbitrary, unbounded set) of property names; like UUIDs or numeric ids. If so, canonicalization could become problematic -- however, should not actually matter regarding use of static mapper.Shinbone
Just to add a hint for the date mapping: Jackson does not seem to use the standard ISO format of "yyyy-MM-dd'T'HH:mm:ss.SSSXXX. If we have to replace the date mapper like me all the time, do not forgot to implement the clone method. I think I had some threading problems if not doing so.Millican
Hi StaxMan, would you help how can be constructed ObjectWriter and ObjectReader from ObjectMapper? I have not found a way using jackson library 2.7.3. Thx in advance.Rexferd
@MiklosKrivan have you looked at ObjectMapper at all?! Methods are named writer() and reader() (and some readerFor(), writerFor()).Shinbone
Sorry @Shinbone you are absolutely right it was my fault that I am missed it. Thx for your feedback.Rexferd
When you say "that you can't be modifying configuration of the mapper once it is shared" do you mean that jackson throws an exception if it is done or that I have to be careful there? I.e. is the instance cloned on every "mapper.with()" call? I read somewhere that this is the recommended way e.g. for request based decisions like pretty printing the json.Mcbroom
There is no mapper.with() call (since "with" in Jackson implies construction of a new instance, and thread-safe execution). But regarding config changes: no checking is made, so config access to ObjectMapper must be guarded. As to "copy()": yes, that creates a fresh new copy that may be fully (re)configured, according to same rules: fully configure it first, then use, and that is fine. There is non-trivial cost associated (since copy can not use any of cached handlers), but it is the safe way, yes.Shinbone
... and as to "why not see if someone tries to reconfig": that would require additional state-keeping, synchronization. For Jackson 3.x it may make sense to use builder-pattern, make ObjectMapper immutable similar to ObjectReader/ObjectWriter: but that's major API change so can't be done with 2.x. Another way to protect against reconfig is to only expose ObjectReaders and ObjectWriters; keep access to mapper only available to small part of system.Shinbone
@Shinbone - Is it ok to instantiate an object mapper inside the toString() method of a class ? Does it depend on how many times toString() is called ?Pleomorphism
@MasterJoe2 given that construction of ObjectMapper is a rather heavy-weight operation, answer depends: yes, it is safe and functionally correct to do that. But it is is also extremely inefficient to do it.Shinbone
@Shinbone - So, would it be better to have a static object mapper for the entire class, given that I don't need to change the mapper's config after its instantiation ? After that, can I use ObjectWriter and ObjectReader instances in my toString() ? Please let me know if I should open a new question for this. thank you.Pleomorphism
@MasterJoe2 static object mapper for entire class sounds better (or even just ObjectWriter) than creating it every time toString() is called. Or even separate helper class with static instance, method, if there are many toString()s to implement across many classes?Shinbone
Would the ObjectWriter and ObjectReader objects have internal synchronization? I mean we would not want the competing threads forces to wait for a very long time.Matthaeus
@AwanBiru They do not have any more internal synchronization than ObjectMapper and in particular if root value serializer/deserializer is fetched for instance should have no contestion. Access to cached (de)serializers can still have some synchronization.Shinbone
@Shinbone yes I just run with profiler on version 2.10.x and did not see lock or monitor captured so far. U mention of root value serializer/deserializer fetched for instance, what would that mean?Matthaeus
@AwanBiru during serialization, deserialization, each Java type has its own kind of (de)serializer. Not all of these can be determined statically (especially for serialization; and in general always for polymorphic types), so some of secondary (de)serializers may be fetched during processing. They get cached and caching requires synchronization. For simple POJOs with scalar types no such access would be needed, for more complex type graphs yes.Shinbone
hopefully this answer stays same for question here #73753293 @BlotchyLaundry
@CheokYanCheng I wouldn't need StaxMan to provide a reference to any Jackson-related answer, as much as i wouldn't when Brian Goetz writes a Java-related answer. After all, StaxMan is one of the main Jackson contributors with a respectable background as a Java professional: cowtowncoder.com/author-cowtowncoder.htmlSemeiology
A
89

Although ObjectMapper is thread safe, I would strongly discourage from declaring it as a static variable, especially in multithreaded application. Not even because it is a bad practice, but because you are running a heavy risk of deadlocking. I am telling it from my own experience. I created an application with 4 identical threads that were getting and processing JSON data from web services. My application was frequently stalling on the following command, according to the thread dump:

Map aPage = mapper.readValue(reader, Map.class);

Beside that, performance was not good. When I replaced static variable with the instance based variable, stalling disappeared and performance quadrupled. I.e. 2.4 millions JSON documents were processed in 40min.56sec., instead of 2.5 hours previously.

Allynallys answered 22/3, 2016 at 18:36 Comment(8)
Gary's answer completely makes sense. But going with a creating an ObjectMapper instance for every class instance may prevent locks but can be really heavy on GC later on (imagine one ObjectMapper instance for every instance of the class you create) . A middle path approach can be, instead of keeping just one (public) static ObjectMapper instance across the application, you can declare a (private) static instance of ObjectMapper in every class. This will reduce a global lock (by distributing the load class-wise), and won't create any new object either, hence light on GC too.Lucerne
And of course, maintaining an ObjectPool is the best way you can go with - thereby giving the best GC and Lock performances. You can refer to the following link for apache-common's ObjectPool implementation. commons.apache.org/proper/commons-pool/api-1.6/org/apache/…Lucerne
I would suggest an alternative: keep static ObjectMapper somewhere, but only get ObjectReader / ObjectWriter instances (via helper methods), retain references to those in other places (or dynamically call). These reader/writer objects are not only fully thread-safe wrt reconfiguration, but also very light-weight (wrt mapper instances). So keeping thousands of references does not add much memory usage.Shinbone
So are call to ObjectReader instances not blocking i.e say objectReader.readTree is called in multithreaded application, threads wont be blocked waiting on another thread, using jackson 2.8.xQuicken
@Quicken no, calls to readXxx() are not blocking and can proceed fully concurrently; esp. for readTree().Shinbone
if readXXX calls aren't blocking, why is static a problem then - why would this "single" instance block threads?Nameplate
hopefully this answers stays same for question here #73753293 cc @LucerneLaundry
I'm not sure why but I was having a memory leak when I had my ObjectMapper instance as a static member variable in a utility class, when I made it an instance per call the memory leak ended and also improved the performance (I'm pretty sure my code is not good and that was probably the cause but still for anyone wondering if this coudl be the cause).Caputo
G
11

A trick I learned from this PR if you don't want to define it as a static final variable but want to save a bit of overhead and guarantee thread safe.

private static final ThreadLocal<ObjectMapper> om = new ThreadLocal<ObjectMapper>() {
    @Override
    protected ObjectMapper initialValue() {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        return objectMapper;
    }
};

public static ObjectMapper getObjectMapper() {
    return om.get();
}

credit to the author.

G answered 1/10, 2018 at 5:19 Comment(3)
But there's a risk of a memory leak as the ObjectMapper will be attached to the thread which may be part of a pool.Chet
@KenstonChoi Should not be a problem, AFAIU. The threads come and go, thread locals come and go with the threads. Depending on the amount of simultaneous threads you may or may not afford the memory, but I don't see "leaks".Charcot
@IvanBalashov, but if the thread is created/returned from/to a thread pool (e.g., containers like Tomcat), it stays. This may be desired in some cases, but something we need to be aware of.Chet
B
1

Although it is safe to declare a static ObjectMapper in terms of thread safety, you should be aware that constructing static Object variables in Java is considered bad practice. For more details, see Why are static variables considered evil? (and if you'd like, my answer)

In short, statics should be avoided because the make it difficult to write concise unit tests. For example, with a static final ObjectMapper, you can't swap out the JSON serialization for dummy code or a no-op.

In addition, a static final prevents you from ever reconfiguring ObjectMapper at runtime. You might not envision a reason for that now, but if you lock yourself into a static final pattern, nothing short of tearing down the classloader will let you re-initialize it.

In the case of ObjectMapper its fine, but in general it is bad practice and there is no advantage over using a singleton pattern or inversion-of-control to manage your long-lived objects.

Bertine answered 24/4, 2013 at 16:39 Comment(5)
I would suggest that although static STATEFUL singletons are typically a danger sign, there are enough reasons why in this case sharing a single (or, small number of) instances makes sense. One may want to use Dependency Injection for that; but at the same time it is worth asking whether there is an actual or potential problem to solve. This especially applies to testing: just because something might be problematic in some case does not mean it is for your usage. So: being aware of problems, great. Assuming "one size fits all", not so good.Shinbone
Obviously understanding the issues involved with any design decision is important, and if you can do something without causing problems for your use case you by definition you won't cause any problems. However I would argue there are no benefits to the use of static instances and it opens the door to significant trouble in the future as your code evolves or is handed off to other developers who might not understand your design decisions. If your framework supports alternatives, there is no reason not to avoid static instances, there certainly are no advantages to them.Bertine
I think this discussion goes into very general and less useful tangents. I have no problem suggesting that it is good to be suspicious of static singletons. I just happen to be very familiar for usage for this particular case and I do not think one can reach specific conclusions from set of general guidelines. So I will leave it at that.Shinbone
Late comment, but wouldn't ObjectMapper in particular disagree with this notion? It exposes readerFor and writerFor which create ObjectReader and ObjectWriter instances on demand. So I'd say put the mapper with the initial config somewhere static, then get readers/writers with per-case config as you need them?Interpreter
@Interpreter yes, that seems like a good pattern to me; treat mapper as factory for creating reader/writer instances for actual use.Shinbone
P
1

com.fasterxml.jackson.databind.type.TypeFactory._hashMapSuperInterfaceChain(HierarchicType)

com.fasterxml.jackson.databind.type.TypeFactory._findSuperInterfaceChain(Type, Class)
  com.fasterxml.jackson.databind.type.TypeFactory._findSuperTypeChain(Class, Class)
     com.fasterxml.jackson.databind.type.TypeFactory.findTypeParameters(Class, Class, TypeBindings)
        com.fasterxml.jackson.databind.type.TypeFactory.findTypeParameters(JavaType, Class)
           com.fasterxml.jackson.databind.type.TypeFactory._fromParamType(ParameterizedType, TypeBindings)
              com.fasterxml.jackson.databind.type.TypeFactory._constructType(Type, TypeBindings)
                 com.fasterxml.jackson.databind.type.TypeFactory.constructType(TypeReference)
                    com.fasterxml.jackson.databind.ObjectMapper.convertValue(Object, TypeReference)

The method _hashMapSuperInterfaceChain in class com.fasterxml.jackson.databind.type.TypeFactory is synchronized. Am seeing contention on the same at high loads.

May be another reason to avoid a static ObjectMapper

Pickett answered 28/4, 2017 at 10:16 Comment(1)
Make sure to check out latest versions (and perhaps indicate version you use here). There have been improvements to locking based on reported issues, and type resolution (f.ex) was fully rewritten for Jackson 2.7. Although in this case, TypeReference is bit expensive thing to use: if possible, resolving it to JavaType would avoid quite a bit of processing (TypeReferences can not be -- unfortunately -- cached for reasons that I won't dig into here), since they are "fully resolved" (super-type, generic typing etc).Shinbone
M
1

This question may be old, but here's what I do.

Hold the ObjectMapper instance in a thread-safe singleton:

public final class JacksonObjectMapperHolder {

  private static volatile JacksonObjectMapperHolder INSTANCE;

  private static final Object MUTEX = new Object();

  public static JacksonObjectMapperHolder getInstance() {
    JacksonObjectMapperHolder instance = INSTANCE;

    if(instance == null) {
      synchronized(MUTEX) {
        instance = INSTANCE;

        if(instance == null) {
          INSTANCE = instance = new JacksonObjectMapperHolder();
        }
      }
    }

    return instance;
  }

  private final ObjectMapper objectMapper = new ObjectMapper();

  private JacksonObjectMapperHolder() {
    super();
  }

  public final ObjectMapper getObjectMapper() {
    return objectMapper;
  }

}
Midnight answered 24/11, 2021 at 21:11 Comment(8)
you don't need: private JacksonObjectMapperHolder() { super(); } ... other than that great solution if you need the ObjectMapper and not the reader/writerDramaturgy
@RoieBeck I partially agree. The class is final, so an implicitly declared constructor is not a problem for inheritance. However, I want to avoid an accidental instantiation, so, I explicitly declare the constructor and mark it private. The tedious call to super is indicative of my choice to avoid implicit code.Midnight
just giving my 2 cents it's your code :), btw I went with the ThreadLocal<OM> solution, as it achieving the same goal, but it is more elegant I think...Dramaturgy
What is the advantage of this approach over just using a static final field for the ObjectMapper which can be accessed from everywhere?Mayenne
ObjectMapper is thread-safe provided the configuration does not change between serialization/deserialization calls. This approach allows for configuration to change between these calls. All I did was put the ObjectMapper instance behind an optimized double-checked locked singleton.Midnight
@Midnight I understand how this works, but I'm not sure if this optimized double-checked locking is the right choice in this situation. If you have a class with private static final ObjectMapper objectMapper = new ObjectMapper(); and a getter for this field, you basically have all you need, or do I miss something here? You can simply do Class.getInstance() from everywhere to access it. So my question, why do you need the complicated 30 loc approach if 3 lines are enough? Or do you just prefer to write it like this out of personal taste and because you are used to do it like this?Mayenne
@Mayenne You are right, this is definitely a personal taste. But I don't think the 3-line approach would always work. For example, say you expect the following order: thread A uses serializer or deserializer, thread B changes the configuration, then thread C serializer or deserializer. The 3-line approach may not guarantee order, right? Disclaimer: I am definitely not a Java multithreading expert, so I may be completely off here.Midnight
@Midnight well you are right, if you change the config of the objectMapper it is not thread-safe. This is also explained by StaxMan in the accepted answer of this question. However, with your code you have exactly the same issue, so there is no difference between the 30 loc approach and the 3 loc approach, as the problem you describe is related to the state of the objectMapper itself.Mayenne

© 2022 - 2024 — McMap. All rights reserved.