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

49
DateFormat실제로 후드 아래에 복제됩니다. 거기에는 좋은 의혹이 있지만, 당신은 덮여 있습니다. :)
StaxMan

3
대기업 응용 프로그램의 단위 / 통합 테스트 중에 이상한 동작에 직면했습니다. ObjectMapper를 정적 최종 클래스 속성으로 넣을 때 PermGen 문제에 직면하기 시작했습니다. 가능한 원인을 설명 할 사람이 있습니까? jackson-databind 버전 2.4.1을 사용하고있었습니다.
Alejo Ceballos

2
@MiklosKrivan 전혀 보지 못했습니까 ObjectMapper?! 메소드의 이름은 writer()reader()(일부와 readerFor(), writerFor()).
StaxMan

2
mapper.with()호출 이 없습니다 (Jackson에서 "with"이므로 새 인스턴스 생성 및 스레드 안전 실행을 의미 함). 그러나 구성 변경과 관련하여 : 검사가 수행되지 않으므로에 대한 구성 액세스 ObjectMapper가 보호되어야합니다. "copy ()"에 관해서는 그렇습니다. 동일한 규칙에 따라 완전히 새로운 (재) 구성 될 수있는 새로운 사본을 만듭니다. 먼저 완전히 구성한 다음 사용하면됩니다. 사본이 캐시 된 핸들러를 사용할 수 없기 때문에 비용이 적게 들지만 안전한 방법입니다.
StaxMan

53

ObjectMapper는 스레드 안전하지만 특히 다중 스레드 응용 프로그램에서 정적 변수로 선언하지 않는 것이 좋습니다. 나쁜 습관이 아니라 교착 상태의 위험이 높기 때문에. 나는 내 자신의 경험에서 그것을 말하고 있습니다. 웹 서비스에서 JSON 데이터를 가져오고 처리하는 4 개의 동일한 스레드로 응용 프로그램을 만들었습니다. 스레드 덤프에 따라 내 응용 프로그램이 다음 명령에서 자주 중단되었습니다.

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

그 외에도 성능이 좋지 않았습니다. 정적 변수를 인스턴스 기반 변수로 바꾸면 정지가 사라지고 성능이 네 배가되었습니다. 즉, 240 만 개의 JSON 문서가 2.5 시간이 아닌 40 분 56 초 동안 처리되었습니다.


15
게리의 대답은 완전히 말이됩니다. 그러나 ObjectMapper모든 클래스 인스턴스에 대해 인스턴스를 만들면 잠금이 방지되지만 나중에 GC에서 실제로 무거울 수 있습니다 (생성하는 클래스의 모든 인스턴스에 대해 하나의 ObjectMapper 인스턴스를 상상해보십시오). 중간 경로 접근 방식은 ObjectMapper응용 프로그램에서 하나의 (공용) 정적 인스턴스를 유지하는 대신 모든 클래스에서 (비공개) 정적 인스턴스를 선언 할 수 있습니다 . 이렇게하면로드를 클래스 단위로 분배하여 전역 잠금이 줄어들고 새 객체도 생성되지 않으므로 GC도 가벼워집니다. ObjectMapper
Abhidemon

그리고 물론, 유지하는 것은 ObjectPool 하여 최상의 제공 - 당신이 갈 수있는 가장 좋은 방법입니다 GCLock공연. apache-common의 ObjectPool구현 에 대해서는 다음 링크를 참조하십시오 . commons.apache.org/proper/commons-pool/api-1.6/org/apache/...
Abhidemon

16
대안을 제안합니다 : ObjectMapper어딘가에 정적을 유지 하지만 도우미 메소드를 통해 ObjectReader/ ObjectWriter인스턴스 만 가져 오고 다른 곳의 참조를 유지하거나 동적으로 호출합니다. 이러한 리더 / 라이터 객체는 스레드로부터 안전하게 wrt를 재구성 할뿐만 아니라 매우 가볍습니다 (wrt mapper 인스턴스). 따라서 수천 개의 참조를 유지해도 많은 메모리 사용량이 추가되지 않습니다.
StaxMan

따라서 ObjectReader 인스턴스에 대한 호출이 차단되지 않습니다. 즉, objectReader.readTree가 멀티 스레드 응용 프로그램에서 호출
된다고

1

스레드 안전성 측면에서 정적 ObjectMapper를 선언하는 것이 안전하지만 Java에서 정적 Object 변수를 구성하는 것은 나쁜 습관으로 간주됩니다. 자세한 내용 은 정적 변수가 악의로 간주되는 이유를 참조하십시오 . (원하는 경우 내 대답 )

요컨대, 간결한 단위 테스트를 작성하기 어렵 기 때문에 정적을 피해야합니다. 예를 들어 정적 최종 ObjectMapper를 사용하면 더미 코드 또는 no-op에 대한 JSON 직렬화를 교체 할 수 없습니다.

또한 정적 파이널을 사용하면 런타임에 ObjectMapper를 다시 구성 할 수 없습니다. 당신은 지금 그 이유를 구상하지 못할 수도 있지만, 정적 최종 패턴에 자신을 고정시키는 경우, 클래스 로더를 찢어 버리지 않아도 다시 초기화 할 수 있습니다.

ObjectMapper의 경우는 좋지만 일반적으로 나쁜 습관이며 오래 지속되는 객체를 관리하기 위해 단일 패턴 또는 제어 역전을 사용하는 것보다 이점이 없습니다.


27
정적 STATEFUL 싱글 톤이 일반적으로 위험 신호이지만이 경우 단일 (또는 적은 수의) 인스턴스를 공유하는 데는 충분한 이유가 있습니다. 이를 위해 Dependency Injection을 사용할 수 있습니다. 그러나 동시에 해결해야 할 실제 또는 잠재적 문제가 있는지 물어볼 가치가 있습니다. 이것은 특히 테스트에 적용됩니다. 어떤 경우에는 문제가있을 수 있다고해서 사용에 문제가있는 것은 아닙니다. 문제를 잘 알고 있습니다. "하나의 크기가 모두에게 적합"하다고 가정하면 그리 좋지 않습니다.
StaxMan

3
디자인 결정과 관련된 문제를 분명히 이해하는 것이 중요하며, 유스 케이스에 문제를 일으키지 않고 무언가를 할 수 있다면 정의에 따라 아무런 문제도 발생하지 않습니다. 그러나 정적 인스턴스를 사용하면 이점이 없으며 나중에 코드가 발전하거나 디자인 결정을 이해하지 못하는 다른 개발자에게 코드가 전달 될 때 심각한 문제가 발생할 수 있습니다. 만약 당신의 프레임 워크가 대안을 지원한다면, 정적 인스턴스를 피하지 않을 이유가 없으며, 그들에게 이점이 없을 것입니다.
JBCP

11
이 논의는 매우 일반적이고 덜 유용한 접선에 해당한다고 생각합니다. 정적 싱글 톤을 의심하는 것이 좋다고 제안하는 데 아무런 문제가 없습니다. 나는이 특별한 경우에 대한 사용법에 매우 익숙하며 일반적인 지침 세트에서 특정 결론에 도달 할 수 없다고 생각합니다. 그래서 나는 그것에 맡길 것입니다.
StaxMan

1
늦은 의견이지만 ObjectMapper가 특히이 개념에 동의하지 않습니까? 그것은 노출 readerFor하고 writerFor있는 작성 ObjectReaderObjectWriter필요에 따라 인스턴스. 그래서 매퍼를 초기 구성으로 정적 위치에 배치 한 다음 필요에 따라 사례별로 구성하여 독자 / 작가를 얻습니다.
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풀의 일부일 수있는 스레드에 연결 되므로 메모리 누수의 위험이 있습니다.
Kenston Choi

@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
최신 버전을 확인하십시오 (여기서 사용중인 버전을 표시하십시오). 보고 된 문제를 기반으로 잠금 기능이 개선되었으며 Jackson 2.7의 형식 확인 (f.ex)이 완전히 다시 작성되었습니다. 이 경우에는 사용하는 TypeReference것이 약간 비쌉니다. 가능하면 JavaType상당히 많은 처리를 피하기 위해 해결하면 ( TypeReference불행히도 여기서 파헤 치지 않는 이유로 캐시 할 수는 없습니다) "완전히 해결되었습니다"(슈퍼 타입, 일반 타이핑 등).
StaxMan
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.