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

Reply via email to