Skip to content

Commit b5c05cc

Browse files
committed
Merge branch '2.7' into 2.8
2 parents 30061d9 + ec327bd commit b5c05cc

File tree

3 files changed

+38
-171
lines changed

3 files changed

+38
-171
lines changed

release-notes/CREDITS

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -476,10 +476,14 @@ Max Drobotov (fizmax@github)
476476
* Reported, contributed fix for #1332: `ArrayIndexOutOfBoundException` for enum by index deser
477477
(2.7.7)
478478

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

484488
Artur Jonkisz (ajonkisz@github)
485489
* Reported #960: `@JsonCreator` not working on a factory with no arguments for ae enum type

release-notes/VERSION

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ Project: jackson-databind
9393
#1359: Improve `JsonNode` deserializer to create `FloatNode` if parser supports
9494
#1362: ObjectReader.readValues()` ignores offset and length when reading an array
9595
(reported by wastevenson@github)
96+
#1363: The static field ClassUtil.sCached can cause a class loader leak
97+
(reported by Stuart D)
9698
#1368: Problem serializing `JsonMappingException` due to addition of non-ignored
9799
`processor` property (added in 2.7)
98100
(reported, suggesed fix by Josh C)

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

Lines changed: 30 additions & 169 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ public final class ClassUtil
1414
{
1515
private final static Class<?> CLS_OBJECT = Object.class;
1616

17+
private final static Annotation[] NO_ANNOTATIONS = new Annotation[0];
18+
private final static Ctor[] NO_CTORS = new Ctor[0];
19+
1720
/*
1821
/**********************************************************
1922
/* Helper classes
@@ -905,57 +908,68 @@ public static boolean isObjectOrPrimitive(Class<?> cls) {
905908

906909
/*
907910
/**********************************************************
908-
/* Caching access to class metadata, added in 2.7
911+
/* Access to various Class definition aspects; possibly
912+
/* cacheable; and attempts was made in 2.7.0 - 2.7.7; however
913+
/* unintented retention (~= memory leak) wrt [databind#1363]
914+
/* resulted in removal of caching
909915
/**********************************************************
910916
*/
911917

912-
/* 17-Sep-2015, tatu: Although access methods should not be significant
913-
* problems for most proper usage, they may become problematic if
914-
* ObjectMapper has to be re-created; and especially so on Android.
915-
* So let's do somewhat aggressive caching.
916-
*/
917-
private final static LRUMap<Class<?>,ClassMetadata> sCached = new LRUMap<Class<?>,ClassMetadata>(48, 48);
918-
919918
/**
920919
* @since 2.7
921920
*/
922921
public static String getPackageName(Class<?> cls) {
923-
return _getMetadata(cls).getPackageName();
922+
Package pkg = cls.getPackage();
923+
return (pkg == null) ? null : pkg.getName();
924924
}
925925

926926
/**
927927
* @since 2.7
928928
*/
929929
public static boolean hasEnclosingMethod(Class<?> cls) {
930-
return _getMetadata(cls).hasEnclosingMethod();
930+
return !isObjectOrPrimitive(cls) && (cls.getEnclosingMethod() != null);
931931
}
932932

933933
/**
934934
* @since 2.7
935935
*/
936936
public static Field[] getDeclaredFields(Class<?> cls) {
937-
return _getMetadata(cls).getDeclaredFields();
937+
return cls.getDeclaredFields();
938938
}
939939

940940
/**
941941
* @since 2.7
942942
*/
943943
public static Method[] getDeclaredMethods(Class<?> cls) {
944-
return _getMetadata(cls).getDeclaredMethods();
944+
return cls.getDeclaredMethods();
945945
}
946946

947947
/**
948948
* @since 2.7
949949
*/
950950
public static Annotation[] findClassAnnotations(Class<?> cls) {
951-
return _getMetadata(cls).getDeclaredAnnotations();
951+
if (isObjectOrPrimitive(cls)) {
952+
return NO_ANNOTATIONS;
953+
}
954+
return cls.getDeclaredAnnotations();
952955
}
953956

954957
/**
955958
* @since 2.7
956959
*/
957960
public static Ctor[] getConstructors(Class<?> cls) {
958-
return _getMetadata(cls).getConstructors();
961+
// Note: can NOT skip abstract classes as they may be used with mix-ins
962+
// and for regular use shouldn't really matter.
963+
if (cls.isInterface() || isObjectOrPrimitive(cls)) {
964+
return NO_CTORS;
965+
}
966+
Constructor<?>[] rawCtors = cls.getDeclaredConstructors();
967+
final int len = rawCtors.length;
968+
Ctor[] result = new Ctor[len];
969+
for (int i = 0; i < len; ++i) {
970+
result[i] = new Ctor(rawCtors[i]);
971+
}
972+
return result;
959973
}
960974

961975
// // // Then methods that do NOT cache access but were considered
@@ -980,7 +994,7 @@ public static Type getGenericSuperclass(Class<?> cls) {
980994
* @since 2.7
981995
*/
982996
public static Type[] getGenericInterfaces(Class<?> cls) {
983-
return _getMetadata(cls).getGenericInterfaces();
997+
return cls.getGenericInterfaces();
984998
}
985999

9861000
/**
@@ -992,22 +1006,7 @@ public static Class<?> getEnclosingClass(Class<?> cls) {
9921006
}
9931007

9941008
private static Class<?>[] _interfaces(Class<?> cls) {
995-
return _getMetadata(cls).getInterfaces();
996-
}
997-
998-
private static ClassMetadata _getMetadata(Class<?> cls)
999-
{
1000-
ClassMetadata md = sCached.get(cls);
1001-
if (md == null) {
1002-
md = new ClassMetadata(cls);
1003-
// tiny optimization, but in case someone concurrently constructed it,
1004-
// let's use that instance, to reduce extra concurrent work.
1005-
ClassMetadata old = sCached.putIfAbsent(cls, md);
1006-
if (old != null) {
1007-
md = old;
1008-
}
1009-
}
1010-
return md;
1009+
return cls.getInterfaces();
10111010
}
10121011

10131012
/*
@@ -1097,144 +1096,6 @@ private static Field locateField(Class<?> fromClass, String expectedName, Class<
10971096
/**********************************************************
10981097
*/
10991098

1100-
/**
1101-
* @since 2.7
1102-
*/
1103-
private final static class ClassMetadata
1104-
{
1105-
private final static Annotation[] NO_ANNOTATIONS = new Annotation[0];
1106-
private final static Ctor[] NO_CTORS = new Ctor[0];
1107-
1108-
private final Class<?> _forClass;
1109-
1110-
private String _packageName;
1111-
private Boolean _hasEnclosingMethod;
1112-
1113-
private Class<?>[] _interfaces;
1114-
private Type[] _genericInterfaces;
1115-
1116-
private Annotation[] _annotations;
1117-
private Ctor[] _constructors;
1118-
private Field[] _fields;
1119-
private Method[] _methods;
1120-
1121-
public ClassMetadata(Class<?> forClass) {
1122-
_forClass = forClass;
1123-
}
1124-
1125-
public String getPackageName() {
1126-
String name = _packageName;
1127-
if (name == null) {
1128-
Package pkg = _forClass.getPackage();
1129-
name = (pkg == null) ? null : pkg.getName();
1130-
if (name == null) {
1131-
name = "";
1132-
}
1133-
_packageName = name;
1134-
}
1135-
return (name == "") ? null : name;
1136-
}
1137-
1138-
// 19-Sep-2015, tatu: Bit of performance improvement, after finding this
1139-
// in profile; maybe 5% in "wasteful" deserialization case
1140-
public Class<?>[] getInterfaces() {
1141-
Class<?>[] result = _interfaces;
1142-
if (result == null) {
1143-
result = _forClass.getInterfaces();
1144-
_interfaces = result;
1145-
}
1146-
return result;
1147-
}
1148-
1149-
// 30-Oct-2015, tatu: Minor performance boost too (5% or less)
1150-
public Type[] getGenericInterfaces() {
1151-
Type[] result = _genericInterfaces;
1152-
if (result == null) {
1153-
result = _forClass.getGenericInterfaces();
1154-
_genericInterfaces = result;
1155-
}
1156-
return result;
1157-
}
1158-
1159-
// 19-Sep-2015, tatu: Modest performance improvement, after finding this
1160-
// in profile; maybe 2-3% in "wasteful" deserialization case
1161-
public Annotation[] getDeclaredAnnotations() {
1162-
Annotation[] result = _annotations;
1163-
if (result == null) {
1164-
result = isObjectOrPrimitive() ? NO_ANNOTATIONS : _forClass.getDeclaredAnnotations();
1165-
_annotations = result;
1166-
}
1167-
return result;
1168-
}
1169-
1170-
// 19-Sep-2015, tatu: Some performance improvement, after finding this
1171-
// in profile; maybe 8-10% in "wasteful" deserialization case
1172-
public Ctor[] getConstructors() {
1173-
Ctor[] result = _constructors;
1174-
if (result == null) {
1175-
// Note: can NOT skip abstract classes as they may be used with mix-ins
1176-
// and for regular use shouldn't really matter.
1177-
if (_forClass.isInterface() || isObjectOrPrimitive()) {
1178-
result = NO_CTORS;
1179-
} else {
1180-
Constructor<?>[] rawCtors = _forClass.getDeclaredConstructors();
1181-
final int len = rawCtors.length;
1182-
result = new Ctor[len];
1183-
for (int i = 0; i < len; ++i) {
1184-
result[i] = new Ctor(rawCtors[i]);
1185-
}
1186-
}
1187-
_constructors = result;
1188-
}
1189-
return result;
1190-
}
1191-
1192-
// 21-Spe-2015, tatu: Surprisingly significant improvement (+10%)...
1193-
public Field[] getDeclaredFields() {
1194-
Field[] fields = _fields;
1195-
if (fields == null) {
1196-
fields = _forClass.getDeclaredFields();
1197-
_fields = fields;
1198-
}
1199-
return fields;
1200-
}
1201-
1202-
// 21-Spe-2015, tatu: Surprisingly significant improvement (+30%)...
1203-
public Method[] getDeclaredMethods() {
1204-
Method[] methods = _methods;
1205-
if (methods == null) {
1206-
methods = _forClass.getDeclaredMethods();
1207-
_methods = methods;
1208-
}
1209-
return methods;
1210-
}
1211-
1212-
// Prominently listed on profiling when not cached, improvement
1213-
// modest, 1-2% range; but at least is measurable so keep it
1214-
public boolean hasEnclosingMethod() {
1215-
Boolean b = _hasEnclosingMethod;
1216-
if (b == null) {
1217-
b = isObjectOrPrimitive() ? Boolean.FALSE : Boolean.valueOf(_forClass.getEnclosingMethod() != null);
1218-
_hasEnclosingMethod = b;
1219-
}
1220-
return b.booleanValue();
1221-
}
1222-
1223-
private boolean isObjectOrPrimitive() {
1224-
return (_forClass == CLS_OBJECT) || _forClass.isPrimitive();
1225-
}
1226-
1227-
/* And then we have a bunch of accessors that did show up in profiling
1228-
* of "wasteful" cases, but for which caching did not yield non-trivial
1229-
* improvements (for tests less than 1% improvement)
1230-
*/
1231-
1232-
// Caching does not seem worthwhile, as per profiling
1233-
// public Type getGenericSuperclass();
1234-
// public Class<?> getDeclaringClass();
1235-
// public Class<?> getEnclosingClass();
1236-
}
1237-
12381099
/**
12391100
* Value class used for caching Constructor declarations; used because
12401101
* caching done by JDK appears to be somewhat inefficient for some use cases.

0 commit comments

Comments
 (0)