La biblioteca de Jackson ObjectMapper
clase de parece ser segura para subprocesos .
¿Esto significa que debería declarar mi ObjectMapper
como un campo estático como este
class Me {
private static final ObjectMapper mapper = new ObjectMapper();
}
en lugar de como un campo de nivel de instancia como este?
class Me {
private final ObjectMapper mapper = new ObjectMapper();
}
ObjectMapper
todavía es seguro para subprocesos después de queObjectMapper#setDateFormat()
se llama. Se sabe queSimpleDateFormat
no es seguro para subprocesos , porObjectMapper
lo que no lo será a menos que clone, por ejemplo,SerializationConfig
antes de cada unowriteValue()
(dudo). ¿Podrías desacreditar mi miedo?DateFormat
de hecho está clonado debajo del capó. Buena sospecha allí, pero estás cubierto. :)ObjectMapper
en absoluto ?! Los métodos se nombranwriter()
yreader()
(y algunosreaderFor()
,writerFor()
).mapper.with()
llamada (ya que "con" en Jackson implica la construcción de una nueva instancia y una ejecución segura para subprocesos). Pero con respecto a los cambios de configuración: no se realiza ninguna comprobación, por lo que seObjectMapper
debe proteger el acceso a la configuración . En cuanto a "copy ()": sí, eso crea una nueva copia nueva que puede ser completamente (re) configurada, de acuerdo con las mismas reglas: configúrela completamente primero, luego úsela, y eso está bien. Hay un costo no trivial asociado (ya que la copia no puede usar ninguno de los controladores en caché), pero es la forma segura, sí.Aunque ObjectMapper es seguro para subprocesos, desaconsejaría declararlo como una variable estática, especialmente en aplicaciones multiproceso. Ni siquiera porque sea una mala práctica, sino porque corres un gran riesgo de bloqueo. Lo digo desde mi propia experiencia. Creé una aplicación con 4 hilos idénticos que obtenían y procesaban datos JSON de los servicios web. Mi aplicación se detenía con frecuencia en el siguiente comando, de acuerdo con el volcado de subprocesos:
Además de eso, el rendimiento no fue bueno. Cuando reemplacé la variable estática con la variable basada en la instancia, el bloqueo desapareció y el rendimiento se cuadruplicó. Es decir, 2,4 millones de documentos JSON se procesaron en 40min.56seg., En lugar de 2.5 horas antes.
fuente
ObjectMapper
instancia para cada instancia de clase puede evitar bloqueos, pero puede ser muy pesado en GC más adelante (imagine una instancia de ObjectMapper para cada instancia de la clase que cree). Un enfoque de ruta intermedia puede ser, en lugar de mantener solo unaObjectMapper
instancia estática (pública) en la aplicación, puede declarar una instancia estática (privada)ObjectMapper
en cada clase . Esto reducirá un bloqueo global (al distribuir la carga a nivel de clase) y tampoco creará ningún objeto nuevo, por lo tanto, también se iluminará el GC.ObjectPool
es la mejor manera de avanzar, brindando lo mejorGC
y el mejorLock
rendimiento. Puede consultar el siguiente enlace para laObjectPool
implementación de apache-common . commons.apache.org/proper/commons-pool/api-1.6/org/apache/…ObjectMapper
algún lugar, pero solo obtenerObjectReader
/ObjectWriter
instancias (a través de métodos auxiliares), retener referencias a aquellos en otros lugares (o llamar dinámicamente). Estos objetos de lector / escritor no solo son una reconfiguración de wrt totalmente segura para subprocesos, sino que también son muy livianos (instancias de mapeador de wrt). Por lo tanto, mantener miles de referencias no agrega mucho uso de memoria.Aunque es seguro declarar un ObjectMapper estático en términos de seguridad de subprocesos, debe tener en cuenta que la construcción de variables Object estáticas en Java se considera una mala práctica. Para obtener más detalles, consulte ¿Por qué las variables estáticas se consideran malas? (y si quieres, mi respuesta )
En resumen, las estadísticas deben evitarse porque dificultan la redacción de pruebas unitarias concisas. Por ejemplo, con un ObjectMapper final estático, no puede intercambiar la serialización JSON por código ficticio o no operativo.
Además, una final estática le impide volver a configurar ObjectMapper en tiempo de ejecución. Puede que no imagines una razón para eso ahora, pero si te encierras en un patrón final estático, nada menos que derribar el cargador de clases te permitirá reiniciarlo.
En el caso de ObjectMapper está bien, pero en general es una mala práctica y no hay ninguna ventaja sobre el uso de un patrón singleton o inversión de control para administrar sus objetos de larga duración.
fuente
readerFor
ywriterFor
que creaObjectReader
eObjectWriter
instancias bajo demanda. ¿Entonces diría que coloque el mapeador con la configuración inicial en algún lugar estático, luego obtenga lectores / escritores con la configuración por caso cuando los necesite?Un truco que aprendí de este PR si no quieres definirlo como una variable final estática pero quieres ahorrar un poco de sobrecarga y garantizar un hilo seguro.
crédito al autor.
fuente
ObjectMapper
que se conectará al hilo que puede ser parte de un grupo.com.fasterxml.jackson.databind.type.TypeFactory._hashMapSuperInterfaceChain (HierarchicType)
El método _hashMapSuperInterfaceChain en la clase com.fasterxml.jackson.databind.type.TypeFactory está sincronizado. Estoy viendo contenciones sobre lo mismo en grandes cargas.
Puede ser otra razón para evitar un ObjectMapper estático
fuente
TypeReference
es algo un poco costoso de usar: si es posible, resolverloJavaType
evitaría una gran cantidad de procesamiento (TypeReference
desafortunadamente, los s no se pueden almacenar en caché por razones que no voy a investigar aquí), ya que están "totalmente resueltos" (supertipo, tipificación genérica, etc.).