Skip to content

Commit ec327bd

Browse files
committed
Fix #1363
1 parent 0d700fd commit ec327bd

File tree

3 files changed

+38
-171
lines changed

3 files changed

+38
-171
lines changed

release-notes/CREDITS

+6-2
Original file line numberDiff line numberDiff line change
@@ -472,7 +472,11 @@ Max Drobotov (fizmax@github)
472472
* Reported, contributed fix for #1332: `ArrayIndexOutOfBoundException` for enum by index deser
473473
(2.7.7)
474474

475+
Stuart Douglas (stuartwdouglas@github)
476+
* Reported #1363: The static field ClassUtil.sCached can cause a class loader leak
477+
(2.7.8)
478+
475479
Josh Caplan (jecaplan@github)
476480
* Reported, suggested fix for #1368: Problem serializing `JsonMappingException` due to addition
477-
of non-ignored `processor` property (added in 2.7)
478-
(2.7.8)
481+
of non-ignored `processor` property (added in 2.7)
482+
(2.7.8)

release-notes/VERSION

+2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ Project: jackson-databind
1010
#1359: Improve `JsonNode` deserializer to create `FloatNode` if parser supports
1111
#1362: ObjectReader.readValues()` ignores offset and length when reading an array
1212
(reported by wastevenson@github)
13+
#1363: The static field ClassUtil.sCached can cause a class loader leak
14+
(reported by Stuart D)
1315
#1368: Problem serializing `JsonMappingException` due to addition of non-ignored
1416
`processor` property (added in 2.7)
1517
(reported, suggesed fix by Josh C)

src/main/java/com/fasterxml/jackson/databind/util/ClassUtil.java

+30-169
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ public final class ClassUtil
1111
{
1212
private final static Class<?> CLS_OBJECT = Object.class;
1313

14+
private final static Annotation[] NO_ANNOTATIONS = new Annotation[0];
15+
private final static Ctor[] NO_CTORS = new Ctor[0];
16+
1417
/*
1518
/**********************************************************
1619
/* Helper classes
@@ -790,57 +793,68 @@ public static boolean isObjectOrPrimitive(Class<?> cls) {
790793

791794
/*
792795
/**********************************************************
793-
/* Caching access to class metadata, added in 2.7
796+
/* Access to various Class definition aspects; possibly
797+
/* cacheable; and attempts was made in 2.7.0 - 2.7.7; however
798+
/* unintented retention (~= memory leak) wrt [databind#1363]
799+
/* resulted in removal of caching
794800
/**********************************************************
795801
*/
796802

797-
/* 17-Sep-2015, tatu: Although access methods should not be significant
798-
* problems for most proper usage, they may become problematic if
799-
* ObjectMapper has to be re-created; and especially so on Android.
800-
* So let's do somewhat aggressive caching.
801-
*/
802-
private final static LRUMap<Class<?>,ClassMetadata> sCached = new LRUMap<Class<?>,ClassMetadata>(48, 48);
803-
804803
/**
805804
* @since 2.7
806805
*/
807806
public static String getPackageName(Class<?> cls) {
808-
return _getMetadata(cls).getPackageName();
807+
Package pkg = cls.getPackage();
808+
return (pkg == null) ? null : pkg.getName();
809809
}
810810

811811
/**
812812
* @since 2.7
813813
*/
814814
public static boolean hasEnclosingMethod(Class<?> cls) {
815-
return _getMetadata(cls).hasEnclosingMethod();
815+
return !isObjectOrPrimitive(cls) && (cls.getEnclosingMethod() != null);
816816
}
817817

818818
/**
819819
* @since 2.7
820820
*/
821821
public static Field[] getDeclaredFields(Class<?> cls) {
822-
return _getMetadata(cls).getDeclaredFields();
822+
return cls.getDeclaredFields();
823823
}
824824

825825
/**
826826
* @since 2.7
827827
*/
828828
public static Method[] getDeclaredMethods(Class<?> cls) {
829-
return _getMetadata(cls).getDeclaredMethods();
829+
return cls.getDeclaredMethods();
830830
}
831831

832832
/**
833833
* @since 2.7
834834
*/
835835
public static Annotation[] findClassAnnotations(Class<?> cls) {
836-
return _getMetadata(cls).getDeclaredAnnotations();
836+
if (isObjectOrPrimitive(cls)) {
837+
return NO_ANNOTATIONS;
838+
}
839+
return cls.getDeclaredAnnotations();
837840
}
838841

839842
/**
840843
* @since 2.7
841844
*/
842845
public static Ctor[] getConstructors(Class<?> cls) {
843-
return _getMetadata(cls).getConstructors();
846+
// Note: can NOT skip abstract classes as they may be used with mix-ins
847+
// and for regular use shouldn't really matter.
848+
if (cls.isInterface() || isObjectOrPrimitive(cls)) {
849+
return NO_CTORS;
850+
}
851+
Constructor<?>[] rawCtors = cls.getDeclaredConstructors();
852+
final int len = rawCtors.length;
853+
Ctor[] result = new Ctor[len];
854+
for (int i = 0; i < len; ++i) {
855+
result[i] = new Ctor(rawCtors[i]);
856+
}
857+
return result;
844858
}
845859

846860
// // // Then methods that do NOT cache access but were considered
@@ -865,7 +879,7 @@ public static Type getGenericSuperclass(Class<?> cls) {
865879
* @since 2.7
866880
*/
867881
public static Type[] getGenericInterfaces(Class<?> cls) {
868-
return _getMetadata(cls).getGenericInterfaces();
882+
return cls.getGenericInterfaces();
869883
}
870884

871885
/**
@@ -877,22 +891,7 @@ public static Class<?> getEnclosingClass(Class<?> cls) {
877891
}
878892

879893
private static Class<?>[] _interfaces(Class<?> cls) {
880-
return _getMetadata(cls).getInterfaces();
881-
}
882-
883-
private static ClassMetadata _getMetadata(Class<?> cls)
884-
{
885-
ClassMetadata md = sCached.get(cls);
886-
if (md == null) {
887-
md = new ClassMetadata(cls);
888-
// tiny optimization, but in case someone concurrently constructed it,
889-
// let's use that instance, to reduce extra concurrent work.
890-
ClassMetadata old = sCached.putIfAbsent(cls, md);
891-
if (old != null) {
892-
md = old;
893-
}
894-
}
895-
return md;
894+
return cls.getInterfaces();
896895
}
897896

898897
/*
@@ -982,144 +981,6 @@ private static Field locateField(Class<?> fromClass, String expectedName, Class<
982981
/**********************************************************
983982
*/
984983

985-
/**
986-
* @since 2.7
987-
*/
988-
private final static class ClassMetadata
989-
{
990-
private final static Annotation[] NO_ANNOTATIONS = new Annotation[0];
991-
private final static Ctor[] NO_CTORS = new Ctor[0];
992-
993-
private final Class<?> _forClass;
994-
995-
private String _packageName;
996-
private Boolean _hasEnclosingMethod;
997-
998-
private Class<?>[] _interfaces;
999-
private Type[] _genericInterfaces;
1000-
1001-
private Annotation[] _annotations;
1002-
private Ctor[] _constructors;
1003-
private Field[] _fields;
1004-
private Method[] _methods;
1005-
1006-
public ClassMetadata(Class<?> forClass) {
1007-
_forClass = forClass;
1008-
}
1009-
1010-
public String getPackageName() {
1011-
String name = _packageName;
1012-
if (name == null) {
1013-
Package pkg = _forClass.getPackage();
1014-
name = (pkg == null) ? null : pkg.getName();
1015-
if (name == null) {
1016-
name = "";
1017-
}
1018-
_packageName = name;
1019-
}
1020-
return (name == "") ? null : name;
1021-
}
1022-
1023-
// 19-Sep-2015, tatu: Bit of performance improvement, after finding this
1024-
// in profile; maybe 5% in "wasteful" deserialization case
1025-
public Class<?>[] getInterfaces() {
1026-
Class<?>[] result = _interfaces;
1027-
if (result == null) {
1028-
result = _forClass.getInterfaces();
1029-
_interfaces = result;
1030-
}
1031-
return result;
1032-
}
1033-
1034-
// 30-Oct-2015, tatu: Minor performance boost too (5% or less)
1035-
public Type[] getGenericInterfaces() {
1036-
Type[] result = _genericInterfaces;
1037-
if (result == null) {
1038-
result = _forClass.getGenericInterfaces();
1039-
_genericInterfaces = result;
1040-
}
1041-
return result;
1042-
}
1043-
1044-
// 19-Sep-2015, tatu: Modest performance improvement, after finding this
1045-
// in profile; maybe 2-3% in "wasteful" deserialization case
1046-
public Annotation[] getDeclaredAnnotations() {
1047-
Annotation[] result = _annotations;
1048-
if (result == null) {
1049-
result = isObjectOrPrimitive() ? NO_ANNOTATIONS : _forClass.getDeclaredAnnotations();
1050-
_annotations = result;
1051-
}
1052-
return result;
1053-
}
1054-
1055-
// 19-Sep-2015, tatu: Some performance improvement, after finding this
1056-
// in profile; maybe 8-10% in "wasteful" deserialization case
1057-
public Ctor[] getConstructors() {
1058-
Ctor[] result = _constructors;
1059-
if (result == null) {
1060-
// Note: can NOT skip abstract classes as they may be used with mix-ins
1061-
// and for regular use shouldn't really matter.
1062-
if (_forClass.isInterface() || isObjectOrPrimitive()) {
1063-
result = NO_CTORS;
1064-
} else {
1065-
Constructor<?>[] rawCtors = _forClass.getDeclaredConstructors();
1066-
final int len = rawCtors.length;
1067-
result = new Ctor[len];
1068-
for (int i = 0; i < len; ++i) {
1069-
result[i] = new Ctor(rawCtors[i]);
1070-
}
1071-
}
1072-
_constructors = result;
1073-
}
1074-
return result;
1075-
}
1076-
1077-
// 21-Spe-2015, tatu: Surprisingly significant improvement (+10%)...
1078-
public Field[] getDeclaredFields() {
1079-
Field[] fields = _fields;
1080-
if (fields == null) {
1081-
fields = _forClass.getDeclaredFields();
1082-
_fields = fields;
1083-
}
1084-
return fields;
1085-
}
1086-
1087-
// 21-Spe-2015, tatu: Surprisingly significant improvement (+30%)...
1088-
public Method[] getDeclaredMethods() {
1089-
Method[] methods = _methods;
1090-
if (methods == null) {
1091-
methods = _forClass.getDeclaredMethods();
1092-
_methods = methods;
1093-
}
1094-
return methods;
1095-
}
1096-
1097-
// Prominently listed on profiling when not cached, improvement
1098-
// modest, 1-2% range; but at least is measurable so keep it
1099-
public boolean hasEnclosingMethod() {
1100-
Boolean b = _hasEnclosingMethod;
1101-
if (b == null) {
1102-
b = isObjectOrPrimitive() ? Boolean.FALSE : Boolean.valueOf(_forClass.getEnclosingMethod() != null);
1103-
_hasEnclosingMethod = b;
1104-
}
1105-
return b.booleanValue();
1106-
}
1107-
1108-
private boolean isObjectOrPrimitive() {
1109-
return (_forClass == CLS_OBJECT) || _forClass.isPrimitive();
1110-
}
1111-
1112-
/* And then we have a bunch of accessors that did show up in profiling
1113-
* of "wasteful" cases, but for which caching did not yield non-trivial
1114-
* improvements (for tests less than 1% improvement)
1115-
*/
1116-
1117-
// Caching does not seem worthwhile, as per profiling
1118-
// public Type getGenericSuperclass();
1119-
// public Class<?> getDeclaringClass();
1120-
// public Class<?> getEnclosingClass();
1121-
}
1122-
1123984
/**
1124985
* Value class used for caching Constructor declarations; used because
1125986
* caching done by JDK appears to be somewhat inefficient for some use cases.

0 commit comments

Comments
 (0)