2009-06-02 13 views
10

Mi comprensión limitada de ThreadLocal es que tiene resource leak issues. Supongo que este problema puede remediarse mediante el uso apropiado de WeakReferences con ThreadLocal (aunque puedo haber entendido mal este punto). Simplemente me gustaría un patrón o ejemplo para usar correctamente ThreadLocal con WeakReference, si existe. Por ejemplo, en este fragmento de código ¿dónde se introduciría WeakReference?ThreadLocal Resource Leak and WeakReference

static class DateTimeFormatter { 
    private static final ThreadLocal<SimpleDateFormat> DATE_PARSER_THREAD_LOCAL = new ThreadLocal<SimpleDateFormat>() { 
     protected SimpleDateFormat initialValue() { 
      return new SimpleDateFormat("yyyy/MM/dd HH:mmz"); 
     } 
    }; 
    public String format(final Date date) { 
     return DATE_PARSER_THREAD_LOCAL.get().format(date); 
    } 
    public Date parse(final String date) throws ParseException 
    { 
     return DATE_PARSER_THREAD_LOCAL.get().parse(date); 
    } 
} 
+1

¿Por qué creen que tiene problemas de recursos? La razón por la que pregunto es porque, según mi experiencia, no he tenido ningún problema. –

+0

Proporcione algunos antecedentes sobre por qué cree que hay una pérdida de memoria. – erickson

+0

Actualizado. Por favor vea el enlace "Problemas de fuga de recursos" arriba. –

Respuesta

27

ThreadLocal utiliza un WeakReference internamente.Si el ThreadLocal no está fuertemente referenciado, se recolectará basura, aunque varios hilos tienen valores almacenados a través de ese ThreadLocal.

Además, ThreadLocal los valores se almacenan realmente en Thread; si un hilo muere, se recopilan todos los valores asociados con ese hilo a través de ThreadLocal.

Si tiene un miembro de la clase final ThreadLocal, esa es una referencia sólida, y no se puede recopilar hasta que se descargue la clase. Pero así es como funciona cualquier miembro de la clase, y no se considera una pérdida de memoria.


Actualización: El problema citado sólo entra en juego cuando el valor almacenado en una ThreadLocal referencia firmemente que ThreadLocal — tipo de una referencia circular.

En este caso, el valor (a SimpleDateFormat) no tiene ninguna referencia hacia atrás al ThreadLocal. No hay pérdida de memoria en este código.

+0

Fuga cuando un valor local de subproceso (indirectamente) hace referencia a su ThreadLocal sigue siendo un error en la implementación de Sun. No estoy seguro de si la implementación loca de Bob Lee en Harmony tiene el mismo problema. –

+0

¿Es un error en ThreadLocal, o la aplicación y su uso de ThreadLocal? Todavía no me he encontrado (y trato de evitar el ThreadLocal), así que aún no estoy seguro. – erickson

+0

Tenga en cuenta que en un entorno Servlet usted NECESITA utilizar un filtro de servlet para borrar ese ThreadLocal; si no lo hace, tendrá una gran cantidad de fugas en las redespliegues –

3

Hay no debería ser un problema.

La referencia ThreadLocal de un hilo se define para existir solo mientras el hilo correspondiente esté vivo (vea el javadoc) - o dicho de otro modo, una vez que el hilo no está vivo, si el ThreadLocal fue la única referencia a ese objeto , entonces el objeto se vuelve elegible para la recolección de basura.

¡O bien ha encontrado un error genuino y debería informarlo, o está haciendo algo diferente!

+0

Considere el caso cuando está ejecutando dentro de un servidor/contenedor de aplicaciones que puede descargar su aplicación (para instalar, digamos, una versión diferente) pero continúa usando los hilos para una nueva versión de la aplicación. –

1

Solo para agregar a lo que dijo @Neil Coffey, esto no debería ser un problema siempre que su instancia de ThreadLocal sea estática. Como sigue llamando a get() en una instancia estática, siempre debe contener la misma referencia a su formateador de fecha simple. Por lo tanto, como dijo Neil, cuando finaliza el subproceso, la única instancia del formateador de fecha simple debe ser elegible para la recolección de basura.

Si tiene números o alguna otra forma de depuración que muestre esta introducción de problemas de recursos, entonces esa es otra historia. Pero creo que no debería ser un problema.

10

Supongo que está saltando estos aros porque SimpleDateFormat es no seguro para subprocesos.

Si bien soy consciente de que no resuelvo su problema anteriormente, ¿puedo sugerirle que consulte Joda para su trabajo de fecha/hora? Joda tiene un mecanismo de formateo de fecha/hora seguro de subprocesos. Tampoco perderá su tiempo aprendiendo la API de Joda, ya que es la base de la nueva propuesta de API de fecha/hora estándar.

+1

+1: Ni siquiera pensé en eso. Es una apuesta justa que este sea el caso, si no fuera por este OP, posiblemente por otra persona. –

0

En su ejemplo, no debería haber ningún problema al utilizar ThreadLocal.

Los elementos locales de subprocesos (¡y también los únicos) se convierten en un problema cuando los locales de subprocesos se configuran en instancias cargadas por un cargador de clases para su descarga posterior. Una situación típica en un contenedor de servlet (como tomcat):

  1. Una aplicación web establece un subproceso local durante el proceso de solicitud.
  2. El subproceso está gestionado por el contenedor.
  3. La aplicación web no debe desplegarse.
  4. El cargador de clases de la aplicación web no se puede guardar, porque hay una referencia a la izquierda: del hilo local a la instancia a su clase a su cargador de clases.

Lo mismo con los controladores únicos (java.sql.DriverManger y JDBC ofrecidos por la aplicación web).

¡Evite tales cosas en particular en entornos que no están bajo su control total!

+3

No, los formateadores no son seguros para subprocesos. Múltiples subprocesos que invocan el método de análisis de una sola instancia es un desastre. – erickson

+0

¡Sin embargo, son buenas las trampas de ThreadLocals! – erickson

+0

bien, no pensé en SimpleDateFormat, pero de todos modos no debería haber ningún problema con el ThreadLocal. –

2

Me doy cuenta de que esto no es estrictamente una respuesta a su pregunta, pero como regla general, no sugeriré utilizar ThreadLocal en una ubicación donde no haya un derribo claro al final de una solicitud/interacción. El clásico está haciendo este tipo de cosas en un contenedor de servlets, a primera vista parece estar bien, pero como los subprocesos se agrupan, se convierte en un problema con el ThreadLocal que cuelga del recurso incluso después de procesar cada solicitud.

Sugerencias:

  1. utilizar un filtro de envoltorio similar para cada interacción para despejar el ThreadLocal al final de cada interacción
  2. Se puede usar un alternativa a SimpleDateFormat como FastDateFormat from commons-lang o Joda como alguien ya la ha sugerido
  3. Simplemente cree un nuevo SimpleDateFormat cada vez que lo necesite. Parece un desperdicio lo sé, pero en la mayoría de las aplicaciones que simplemente no notará la diferencia
0

limpia el hilo local después de usarlo, añadir filtro de servlet para hacerlo:

 
protected ThreadLocal myThreadLocal = new ThreadLocal(); 

public void doFilter (ServletRequest req, ServletResponse res, chain) throws IOException, ServletException { 
    try { 
     // Set ThreadLocal (eg. to store heavy computation result done only once per request) 
     myThreadLocal.set(context()); 

     chain.doFilter(req, res); 
    } finally { 
     // Important : cleanup ThreaLocal to prevent memory leak 
     userIsSmartTL.remove(); 
    } 
}