I tweaked it for my web app and defined the class as follows, and I call in my @PreDestroy method of my 'only' CDI @ApplicationScoped bean.
/* * To change this template, choose Tools | Templates * and open the template in the editor. */ package pf; import com.google.gson.Gson; import java.lang.ref.WeakReference; import java.lang.reflect.Array; import java.lang.reflect.Field; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * * @author Administrator * * google-gson issue # 402: Memory Leak in web application; comment # 25 * https://code.google.com/p/google-gson/issues/detail?id=402 */ public class GSONThreadLocalImmolater { final Logger logger = LoggerFactory.getLogger(GSONThreadLocalImmolater.class); Boolean debug; public GSONThreadLocalImmolater() { debug = true; } public Integer immolate() { int count = 0; try { final Field threadLocalsField = Thread.class.getDeclaredField("threadLocals"); threadLocalsField.setAccessible(true); final Field inheritableThreadLocalsField = Thread.class.getDeclaredField("inheritableThreadLocals"); inheritableThreadLocalsField.setAccessible(true); for (final Thread thread : Thread.getAllStackTraces().keySet()) { count += clear(threadLocalsField.get(thread)); count += clear(inheritableThreadLocalsField.get(thread)); } logger.info("immolated " + count + " GSON values in ThreadLocals"); } catch (Exception e) { throw new Error("GSONThreadLocalImmolater.immolate()", e); } return count; } private int clear(final Object threadLocalMap) throws Exception { if (threadLocalMap == null) return 0; int count = 0; final Field tableField = threadLocalMap.getClass().getDeclaredField("table"); tableField.setAccessible(true); final Object table = tableField.get(threadLocalMap); for (int i = 0, length = Array.getLength(table); i < length; ++i) { final Object entry = Array.get(table, i); if (entry != null) { final Object threadLocal = ((WeakReference)entry).get(); if (threadLocal != null && threadLocal.getClass().getEnclosingClass() == Gson.class) { log(i, threadLocal); Array.set(table, i, null); ++count; } else if (threadLocal != null) { log(i, threadLocal); Array.set(table, i, null); } } } return count; } private void log(int i, final Object threadLocal) { if (!debug) { return; } if (threadLocal.getClass() != null && threadLocal.getClass().getEnclosingClass() != null && threadLocal.getClass().getEnclosingClass().getName() != null) { logger.info("threadLocalMap(" + i + "): " + threadLocal.getClass().getEnclosingClass().getName()); } else if (threadLocal.getClass() != null && threadLocal.getClass().getName() != null) { logger.info("threadLocalMap(" + i + "): " + threadLocal.getClass().getName()); } else { logger.info("threadLocalMap(" + i + "): cannot identify threadlocal class name"); } } } On Fri, Mar 29, 2013 at 3:13 PM, Howard W. Smith, Jr. < [email protected]> wrote: > Wow, someone responded to google-gson issue # 402[1]: Memory Leak in web > application; comment # 25, and shared the following, and this works in my > web app! > > import com.google.gson.Gson; > import java.lang.ref.WeakReference; > import java.lang.reflect.Array; > import java.lang.reflect.Field; > > class GSONThreadLocalImmolater { > > public static void main(final String[] args) { > new Gson().toJson("blah"); > immolate(); // should print 1 > } > > static void immolate() { > try { > int count = 0; > final Field threadLocalsField = Thread.class. > getDeclaredField("threadLocals"); > threadLocalsField.setAccessible(true); > final Field inheritableThreadLocalsField = Thread. > class.getDeclaredField("inheritableThreadLocals"); > inheritableThreadLocalsField.setAccessible(true); > for (final Thread thread : Thread. > getAllStackTraces().keySet()) { > count += clear(threadLocalsField.get( > thread)); > count += clear( > inheritableThreadLocalsField.get(thread)); > } > System.out.println("immolated " + count + " GSON > values in ThreadLocals"); > } catch (final Exception e) { > throw new Error("die", e); > } > } > > private static int clear(final Object threadLocalMap) throws > Exception { > if (threadLocalMap == null) > return 0; > int count = 0; > final Field tableField = threadLocalMap.getClass(). > getDeclaredField("table"); > tableField.setAccessible(true); > final Object table = tableField.get(threadLocalMap); > for (int i = 0, length = Array.getLength(table); i <length > ; ++i) { > final Object entry = Array.get(table, i); > if (entry != null) { > final Object threadLocal = ((WeakReference > )entry).get(); > if (threadLocal != null && threadLocal. > getClass().getEnclosingClass() == Gson.class) { > Array.set(table, i, null); > ++count; > } > } > } > return count; > } > } > > > [1] https://code.google.com/p/google-gson/issues/detail?id=402 > > > On Tue, Dec 11, 2012 at 12:00 AM, Howard W. Smith, Jr. < > [email protected]> wrote: > >> Please see exceptions below that usually occur when I shutdown TomEE (to >> deploy/drop a new WAR file). I am already using @PreDestroy on CDI >> @ApplicationScoped beanto stop other services. I think these exceptions are >> telling me that it might be best for me to do some type of stop-or-destroy >> Google Apss API as well. Please confirm. Thanks. >> >> >> SEVERE: The web application [/mcmsweb] appears to have started a thread >> named [Thread-59] but has failed to stop it. This is very likely to create >> a memory leak. >> Dec 10, 2012 11:16:12 PM org.apache.catalina.loader.WebappClassLoader >> clearReferencesThreads >> SEVERE: The web application [/mcmsweb] appears to have started a thread >> named [AWT-Windows] but has failed to stop it. This is very likely to >> create a memory leak. >> Dec 10, 2012 11:16:12 PM org.apache.catalina.loader.WebappClassLoader >> checkThreadLocalMapForLeaks >> SEVERE: The web application [/mcmsweb] created a ThreadLocal with key of >> type [com.google.api.client.util.escape.Platform$1] (value >> [com.google.api.client.util.escape.Platform$1@1757c6b]) and a value of >> type [char[]] (value [[C@3b9319]) but failed to remove it when the web >> application was stopped. Threads are going to be renewed over time to try >> and avoid a probable memory leak. >> Dec 10, 2012 11:16:12 PM org.apache.catalina.loader.WebappClassLoader >> checkThreadLocalMapForLeaks >> SEVERE: The web application [/mcmsweb] created a ThreadLocal with key of >> type [com.google.api.client.util.escape.Platform$1] (value >> [com.google.api.client.util.escape.Platform$1@1757c6b]) and a value of >> type [char[]] (value [[C@13656a2]) but failed to remove it when the web >> application was stopped. Threads are going to be renewed over time to try >> and avoid a probable memory leak. >> Dec 10, 2012 11:16:12 PM org.apache.catalina.loader.WebappClassLoader >> checkThreadLocalMapForLeaks >> SEVERE: The web application [/mcmsweb] created a ThreadLocal with key of >> type [com.google.api.client.util.escape.Platform$1] (value >> [com.google.api.client.util.escape.Platform$1@1757c6b]) and a value of >> type [char[]] (value [[C@71c1ff]) but failed to remove it when the web >> application was stopped. Threads are going to be renewed over time to try >> and avoid a probable memory leak. >> Dec 10, 2012 11:16:12 PM org.apache.catalina.loader.WebappClassLoader >> checkThreadLocalMapForLeaks >> SEVERE: The web application [/mcmsweb] created a ThreadLocal with key of >> type [com.google.api.client.util.escape.Platform$1] (value >> [com.google.api.client.util.escape.Platform$1@1757c6b]) and a value of >> type [char[]] (value [[C@77759b]) but failed to remove it when the web >> application was stopped. Threads are going to be renewed over time to try >> and avoid a probable memory leak. >> Dec 10, 2012 11:16:12 PM org.apache.catalina.loader.WebappClassLoader >> checkThreadLocalMapForLeaks >> SEVERE: The web application [/mcmsweb] created a ThreadLocal with key of >> type [com.google.gson.Gson$1] (value [com.google.gson.Gson$1@ca428a]) >> and a value of type [java.util.HashMap] (value [{}]) but failed to remove >> it when the web application was stopped. Threads are going to be renewed >> over time to try and avoid a probable memory leak. >> Dec 10, 2012 11:16:12 PM org.apache.catalina.loader.WebappClassLoader >> checkThreadLocalMapForLeaks >> SEVERE: The web application [/mcmsweb] created a ThreadLocal with key of >> type [com.google.api.client.util.escape.Platform$1] (value >> [com.google.api.client.util.escape.Platform$1@1757c6b]) and a value of >> type [char[]] (value [[C@d685eb]) but failed to remove it when the web >> application was stopped. Threads are going to be renewed over time to try >> and avoid a probable memory leak. >> Dec 10, 2012 11:16:12 PM org.apache.catalina.loader.WebappClassLoader >> checkThreadLocalMapForLeaks >> SEVERE: The web application [/mcmsweb] created a ThreadLocal with key of >> type [com.google.api.client.util.escape.Platform$1] (value >> [com.google.api.client.util.escape.Platform$1@1757c6b]) and a value of >> type [char[]] (value [[C@1f21c3f]) but failed to remove it when the web >> application was stopped. Threads are going to be renewed over time to try >> and avoid a probable memory leak. >> Dec 10, 2012 11:16:12 PM org.apache.catalina.loader.WebappClassLoader >> checkThreadLocalMapForLeaks >> SEVERE: The web application [/mcmsweb] created a ThreadLocal with key of >> type [com.google.api.client.util.escape.Platform$1] (value >> [com.google.api.client.util.escape.Platform$1@1757c6b]) and a value of >> type [char[]] (value [[C@1c7a6e]) but failed to remove it when the web >> application was stopped. Threads are going to be renewed over time to try >> and avoid a probable memory leak. >> >> >
