JacksonのObjectMapperを静的フィールドとして宣言する必要がありますか?


361

JacksonライブラリのObjectMapperクラスはスレッドセーフのようです。

これは私ObjectMapperをこのような静的フィールドとして宣言する必要があることを意味しますか

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

このようなインスタンスレベルのフィールドとしてではなく?

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

回答:


505

はい、それは安全で推奨されています。

参照したページの唯一の注意点は、マッパーが共有されるとマッパーの構成を変更できないことです。しかし、設定を変更していないので問題ありません。構成を変更する必要がある場合は、静的ブロックから変更することもできます。

編集:(2013/10)

使用:2.0以上で、上記のより良い方法があることに注目することによって増強することができるObjectWriterObjectReaderオブジェクトによって構築することができますObjectMapper。これらは完全に不変で、スレッドセーフです。つまり、理論的にはスレッドセーフの問題を引き起こすことは不可能ObjectMapperです(コードがインスタンスを再構成しようとすると発生する可能性があります)。


23
@StaxMan:が呼び出されたObjectMapper後もスレッドセーフかどうかは少し心配ですObjectMapper#setDateFormat()SimpleDateFormatスレッドセーフはないことがわかっているためObjectMapperたとえばSerializationConfigそれぞれの前にクローンを作成しない限り、安全はありませんwriteValue()(私は疑問です)。私の恐怖を暴くことができますか?
dma_k 2013

49
DateFormat確かに内部で複製されています。そこに良い疑いがありますが、あなたは覆われています。:)
StaxMan 2013

3
大規模なエンタープライズアプリケーションの単体/統合テスト中に、奇妙な動作に直面しました。ObjectMapperを静的な最終クラス属性として配置すると、PermGenの問題に直面し始めました。誰かが考えられる原因を説明したいと思いますか?jackson-databindバージョン2.4.1を使用していました。
Alejo Ceballos

2
@MiklosKrivan見たことあるObjectMapper?!メソッドにはwriter()and reader()(および一部のreaderFor()writerFor())という名前が付けられます。
StaxMan 2016年

2
mapper.with()呼び出しはありません(Jacksonの "with"は新しいインスタンスの構築とスレッドセーフな実行を意味するため)。ただし、構成の変更に関しては、チェックは行われないため、への構成アクセスObjectMapperは保護する必要があります。"copy()"について:はい、同じルールに従って完全に(再)構成できる新しいコピーを作成します。最初に完全に構​​成してから使用します。これで問題ありません。コピーにはキャッシュされたハンドラーを使用できないため、自明ではないコストが伴いますが、安全な方法です。
StaxMan 2017年

53

ObjectMapperはスレッドセーフですが、特にマルチスレッドアプリケーションでは、静的変数として宣言しないことを強くお勧めします。これは悪い習慣ではありませんが、デッドロックの大きなリスクを負っているからです。私自身の経験から言っています。WebサービスからJSONデータを取得して処理する4つの同一のスレッドでアプリケーションを作成しました。スレッドダンプによると、次のコマンドでアプリケーションが頻繁に停止しています。

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

その上、パフォーマンスは良くありませんでした。静的変数をインスタンスベースの変数に置き換えると、ストールがなくなり、パフォーマンスが4倍になりました。つまり、240万のJSONドキュメントが、以前の2.5時間ではなく、40分56秒で処理されました。


15
ゲイリーの答えは完全に理にかなっています。ただしObjectMapper、すべてのクラスインスタンスのインスタンスを作成すると、ロックが防止される可能性がありますが、後でGCが非常に重くなる可能性があります(作成するクラスのインスタンスごとに1つのObjectMapperインスタンスを想像してください)。ミドルパスアプローチではObjectMapper、アプリケーション全体で1つの(パブリック)静的インスタンスだけを保持する代わりに、すべてのクラスで(プライベート)静的インスタンスを宣言できます。これにより、(クラスごとに負荷を分散することにより)グローバルロックが削減され、新しいオブジェクトも作成されないため、GCも軽量化されます。ObjectMapper
Abhidemon 2016年

そしてもちろん、維持することは ObjectPool 、それによって最高の与える-あなたが行くことができる最善の方法であるGCLock性能が。apache-commonのObjectPool実装については、次のリンクを参照してください。 commons.apache.org/proper/commons-pool/api-1.6/org/apache/...
Abhidemon

16
私は代替案を提案します:静的なObjectMapper場所をどこかに維持しますが、(ヘルパーメソッドを介して)ObjectReader/ ObjectWriterインスタンスのみを取得し、他の場所のインスタンスへの参照を保持します(または動的に呼び出します)。これらのリーダー/ライターオブジェクトは、完全にスレッドセーフなwrt再構成であるだけでなく、非常に軽量です(書き込みマッパーインスタンス)。したがって、何千もの参照を保持しても、メモリ使用量はそれほど増えません。
StaxMan 2017年

つまり、ObjectReaderインスタンスの呼び出しはブロックされていません。つまり、objectReader.readTreeがマルチスレッドアプリケーションで呼び出された場合、jackson 2.8.xを使用して別のスレッドで待機しているスレッドはブロックされません
Xephonia

1

スレッドセーフの観点から静的ObjectMapperを宣言することは安全ですが、Javaで静的オブジェクト変数を構築することは悪い習慣と見なされることに注意してください。詳細については、静的変数が悪と見なされる理由を参照してください(そしてあなたが望むなら、私の答え

つまり、簡潔な単体テストを書くことが難しくなるので、静的要素は避けるべきです。たとえば、静的な最終ObjectMapperでは、JSONシリアル化をダミーコードまたは何もしないためにスワップアウトすることはできません。

さらに、静的ファイナルは、実行時にObjectMapperを再構成することを防ぎます。現時点ではその理由を想定していないかもしれませんが、静的な最終パターンに固執した場合、クラスローダーを破棄するだけで再初期化できます。

ObjectMapperの場合は問題ありませんが、一般的には不適切な方法であり、シングルトンパターンまたは制御の反転を使用して、長期間有効なオブジェクトを管理することに勝る利点はありません。


27
静的なSTATEFULシングルトンは通常危険標識ですが、この場合に単一(または少数)のインスタンスを共有することが理にかなっている十分な理由があることをお勧めします。そのために依存性注入を使用したい場合があります。しかし同時に、解決すべき実際の問題または潜在的な問題があるかどうかを尋ねる価値があります。これは特にテストに当てはまります。場合によっては問題が発生する可能性があるからといって、それがユーザーの使用に適しているとは限りません。だから:問題を意識して、素晴らしい。「1つのサイズですべてに合う」と仮定すると、あまり良くありません。
StaxMan 2013

3
明らかに、設計の決定に伴う問題を理解することは重要です。ユースケースで問題を発生させることなく何かを実行できる場合、当然のことながら問題は発生しません。ただし、静的インスタンスを使用してもメリットはなく、コードが進化したり、設計の決定を理解していない可能性がある他の開発者に引き渡されたりすると、重大な問題が発生する可能性があります。フレームワークが代替をサポートしている場合、静的インスタンスを回避しない理由はなく、確かにそれらに利点はありません。
JBCP 2013

11
この議論は非常に一般的で有用性の低い接線に入ると思います。静的なシングルトンを疑うのは良いことだと言っても問題ありません。私はたまたまこの特定のケースの使用法に非常に精通しており、一般的なガイドラインのセットから特定の結論に達することができないと思います。それで、それはそのままにしておきます。
StaxMan 2013

1
最近のコメントですが、ObjectMapperは特にこの概念に同意しませんか?それは公開しreaderFor、オンデマンドwriterForで作成ObjectReaderおよびObjectWriterインスタンス化します。したがって、マッパーを初期構成の静的な場所に置き、必要に応じてケースごとの構成でリーダー/ライターを取得しますか?
Carighan

1

静的な最終変数として定義したくないが、オーバーヘッドを少し節約してスレッドセーフを保証したい場合は、このPRから学んだトリック。

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();
}

著者への信用。


2
ただしObjectMapper、はプールの一部である可能性があるスレッドにアタッチされるため、メモリリークのリスクがあります。
ケンストンチェ

@KenstonChoi問題ではない、AFAIU。スレッドは出入りし、スレッドローカルはスレッドに出入りします。同時スレッドの量に応じて、メモリの余裕がある場合とない場合がありますが、「リーク」はありません。
Ivan Balashov

2
@IvanBalashov、ただしスレッドがスレッドプール(Tomcatなどのコンテナー)から作成/スレッドプールに返される場合、スレッドはそのまま残ります。これは場合によっては望ましいかもしれませんが、注意する必要があります。
Kenston Choi

-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)

クラスcom.fasterxml.jackson.databind.type.TypeFactoryのメソッド_hashMapSuperInterfaceChain が同期されます。高負荷で同じ問題が発生しています。

静的ObjectMapperを回避するもう1つの理由かもしれません


1
必ず最新バージョンをチェックアウトしてください(おそらく、ここで使用しているバージョンを示してください)。報告された問題に基づいてロックが改善され、型解決(f.ex)がJackson 2.7で完全に書き直されました。この場合、TypeReference使用するには少しコストがかかりますが、可能であれば、解決してJavaTypeかなりの処理を回避します(TypeReferenceここでは詳しく説明しないので、残念ながらsをキャッシュすることはできません)。 「完全に解決」されている(スーパータイプ、ジェネリックタイピングなど)。
StaxMan 2017年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.