Lines 17-28
Link Here
|
17 |
package org.apache.catalina.core; |
17 |
package org.apache.catalina.core; |
18 |
|
18 |
|
19 |
|
19 |
|
|
|
20 |
import java.lang.reflect.Field; |
21 |
|
20 |
import java.util.ArrayList; |
22 |
import java.util.ArrayList; |
21 |
import java.util.List; |
23 |
import java.util.List; |
22 |
import java.util.Locale; |
24 |
import java.util.Locale; |
23 |
import java.util.Map; |
25 |
import java.util.Map; |
24 |
import java.util.WeakHashMap; |
26 |
import java.util.WeakHashMap; |
25 |
import java.util.regex.Pattern; |
27 |
import java.util.regex.Pattern; |
|
|
28 |
import java.util.concurrent.ConcurrentHashMap; |
26 |
|
29 |
|
27 |
import org.apache.catalina.Container; |
30 |
import org.apache.catalina.Container; |
28 |
import org.apache.catalina.Context; |
31 |
import org.apache.catalina.Context; |
Lines 635-640
Link Here
|
635 |
*/ |
638 |
*/ |
636 |
public String[] findReloadedContextMemoryLeaks() { |
639 |
public String[] findReloadedContextMemoryLeaks() { |
637 |
|
640 |
|
|
|
641 |
preventObjectStreamFalsePositives(); |
642 |
|
638 |
System.gc(); |
643 |
System.gc(); |
639 |
|
644 |
|
640 |
List<String> result = new ArrayList<String>(); |
645 |
List<String> result = new ArrayList<String>(); |
Lines 653-658
Link Here
|
653 |
} |
658 |
} |
654 |
|
659 |
|
655 |
/** |
660 |
/** |
|
|
661 |
* This method performs additional cleanup to work around the issue described in LBCORE-205 (Logback JIRA) |
662 |
* and https://issues.apache.org/bugzilla/show_bug.cgi?id=51195 |
663 |
* |
664 |
* The class java.io.ObjectStreamClass, which is used for serialization, has an internal class Caches |
665 |
* that keeps SoftReferences to classes previously (de)serialized. This causes a semi-leak since it prevents |
666 |
* unloading of those classes (and their classloader, including all other loaded classes) until memory is |
667 |
* running really low, forcing the SoftReferences to be collected by the garbage collection. |
668 |
* |
669 |
* This, in turn, causes a leak warning in Tomcat 7 leak-detection. |
670 |
* One could argue that this is a false positive since the webapp will, at some point, be completely collected. |
671 |
* |
672 |
* Regardless of this point, the fix below will, beside fixing the warning, relieve stress from the garbage |
673 |
* collector, since the amount of memory kept by a webapp classloader can be quite significant. |
674 |
* Freeing up those resources as soon as possible does make sense in any case. |
675 |
* |
676 |
* Calling the method below should be quite safe. |
677 |
* All relevant exceptions (including SecurityException) are handled properly. |
678 |
* Clearing the maps in Caches is a safe operation, too, since they are instances of ConcurrentMap and clear() |
679 |
* is performed atomic. Sudden cleanup of those maps would occur "naturally" in case of low memory, anyway. |
680 |
* |
681 |
* The only small "downside" will be a little bit of slowdown in case of further (de)serialization directly |
682 |
* afterwards since the caches will need to be repopulated. |
683 |
*/ |
684 |
private void preventObjectStreamFalsePositives() { |
685 |
Throwable t=null; |
686 |
String className = "java.io.ObjectStreamClass$Caches"; |
687 |
try { |
688 |
Class clazz = Class.forName(className); |
689 |
clearStaticConcurrentHashMap(clazz, "localDescs"); |
690 |
clearStaticConcurrentHashMap(clazz, "reflectors"); |
691 |
} catch (ClassNotFoundException e) { |
692 |
t = e; |
693 |
} catch (SecurityException e) { |
694 |
t = e; |
695 |
} |
696 |
|
697 |
if(t != null) { |
698 |
// ignore |
699 |
// this would be the right place to add some info that flushing |
700 |
// did not work out. |
701 |
// no harm is done, though |
702 |
} |
703 |
} |
704 |
|
705 |
|
706 |
private void clearStaticConcurrentHashMap(Class clazz, String fieldName) { |
707 |
Throwable t=null; |
708 |
try { |
709 |
Field field = clazz.getDeclaredField(fieldName); |
710 |
field.setAccessible(true); |
711 |
Object value = field.get(null); |
712 |
if(value instanceof ConcurrentHashMap) { |
713 |
ConcurrentHashMap map = (ConcurrentHashMap) value; |
714 |
map.clear(); |
715 |
} else { |
716 |
// ignore |
717 |
// this would be the right place to add some info that flushing |
718 |
// of the field did not work out. |
719 |
// no harm is done, though |
720 |
} |
721 |
} catch (NoSuchFieldException e) { |
722 |
t = e; |
723 |
} catch (IllegalAccessException e) { |
724 |
t = e; |
725 |
} catch (SecurityException e) { |
726 |
t = e; |
727 |
} |
728 |
if(t != null) { |
729 |
// ignore |
730 |
// this would be the right place to add some info that flushing |
731 |
// of the field did not work out. |
732 |
// no harm is done, though |
733 |
} |
734 |
} |
735 |
|
736 |
/** |
656 |
* Return the set of alias names for this Host. If none are defined, |
737 |
* Return the set of alias names for this Host. If none are defined, |
657 |
* a zero length array is returned. |
738 |
* a zero length array is returned. |
658 |
*/ |
739 |
*/ |