Skip to content

Commit a4b5de9

Browse files
authored
Excessive memory in ODataConventionModelBuilder BuildDerivedTypesMapping (#2436)
* Excessive memory in ODataConventionModelBuilder BuildDerivedTypesMapping * Resolve the address * Address comments related the empty enumerable
1 parent d02bcd1 commit a4b5de9

2 files changed

Lines changed: 73 additions & 46 deletions

File tree

src/Microsoft.AspNet.OData.Shared/Builder/ODataConventionModelBuilder.cs

Lines changed: 41 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ public partial class ODataConventionModelBuilder : ODataModelBuilder
8686
private bool _isQueryCompositionMode;
8787

8888
// build the mapping between type and its derived types to be used later.
89-
private Lazy<IDictionary<Type, List<Type>>> _allTypesWithDerivedTypeMapping;
89+
private Lazy<IDictionary<Type, Type[]>> _allTypesWithDerivedTypeMapping;
9090

9191
/// <summary>
9292
/// Initializes a new <see cref="ODataConventionModelBuilder"/>.
@@ -133,7 +133,7 @@ internal void Initialize(IWebApiAssembliesResolver assembliesResolver, bool isQu
133133
_mappedTypes = new HashSet<StructuralTypeConfiguration>();
134134
_ignoredTypes = new HashSet<Type>();
135135
ModelAliasingEnabled = true;
136-
_allTypesWithDerivedTypeMapping = new Lazy<IDictionary<Type, List<Type>>>(
136+
_allTypesWithDerivedTypeMapping = new Lazy<IDictionary<Type, Type[]>>(
137137
() => BuildDerivedTypesMapping(assembliesResolver),
138138
isThreadSafe: false);
139139
}
@@ -603,7 +603,7 @@ internal void MapDerivedTypes(StructuralTypeConfiguration structuralType)
603603
StructuralTypeConfiguration baseType = typeToBeVisited.Dequeue();
604604
visitedTypes.Add(baseType.ClrType);
605605

606-
List<Type> derivedTypes;
606+
Type[] derivedTypes;
607607
if (_allTypesWithDerivedTypeMapping.Value.TryGetValue(baseType.ClrType, out derivedTypes))
608608
{
609609
foreach (Type derivedType in derivedTypes)
@@ -825,7 +825,7 @@ internal void ReconfigInferedEntityTypeAsComplexType(Type propertyType)
825825
Type currentType = typeToBeVisited.Dequeue();
826826
visitedTypes.Add(currentType);
827827

828-
List<Type> derivedTypes;
828+
Type[] derivedTypes;
829829
if (_allTypesWithDerivedTypeMapping.Value.TryGetValue(currentType, out derivedTypes))
830830
{
831831
foreach (Type derivedType in derivedTypes)
@@ -865,7 +865,7 @@ internal bool InferEdmTypeFromDerivedTypes(Type propertyType, ref PropertyKind p
865865
Type currentType = typeToBeVisited.Dequeue();
866866
visitedTypes.Add(currentType);
867867

868-
List<Type> derivedTypes;
868+
Type[] derivedTypes;
869869
if (_allTypesWithDerivedTypeMapping.Value.TryGetValue(currentType, out derivedTypes))
870870
{
871871
foreach (Type derivedType in derivedTypes)
@@ -1080,21 +1080,48 @@ private void ReapplyPropertyConvention(PropertyConfiguration property,
10801080
}
10811081
}
10821082

1083-
private static Dictionary<Type, List<Type>> BuildDerivedTypesMapping(IWebApiAssembliesResolver assemblyResolver)
1083+
private static Dictionary<Type, Type[]> BuildDerivedTypesMapping(IWebApiAssembliesResolver assemblyResolver)
10841084
{
1085-
IEnumerable<Type> allTypes = TypeHelper.GetLoadedTypes(assemblyResolver).Where(t => TypeHelper.IsVisible(t) && TypeHelper.IsClass(t) && t != typeof(object));
1086-
Dictionary<Type, List<Type>> allTypeMapping = allTypes.Distinct().ToDictionary(k => k, k => new List<Type>());
1087-
1088-
foreach (Type type in allTypes)
1085+
Predicate<Type> verifyType = t => TypeHelper.IsVisible(t) && TypeHelper.IsClass(t) && t != typeof(object);
1086+
1087+
// The dictionary is allocated to contain all visible reference types which is not system.Object, each with an List<Type> allocated.
1088+
// The list is used to keep derived types.
1089+
// In a common scenario, there are about 10% types with derived types.
1090+
// So, we don't need to keep a large number of empty list in the dictionary.
1091+
Dictionary<Type, List<Type>> temp = new Dictionary<Type, List<Type>>();
1092+
foreach (Type type in TypeHelper.GetLoadedTypes(assemblyResolver))
10891093
{
1090-
List<Type> derivedTypes;
1091-
if (TypeHelper.GetBaseType(type) != null && allTypeMapping.TryGetValue(TypeHelper.GetBaseType(type), out derivedTypes))
1094+
if (type != null && verifyType(type))
10921095
{
1093-
derivedTypes.Add(type);
1096+
Type baseType = TypeHelper.GetBaseType(type);
1097+
if (baseType != null)
1098+
{
1099+
List<Type> list;
1100+
if (!temp.TryGetValue(baseType, out list))
1101+
{
1102+
if (verifyType(baseType))
1103+
{
1104+
list = new List<Type>(1);
1105+
temp[baseType] = list;
1106+
}
1107+
}
1108+
1109+
if (list != null)
1110+
{
1111+
list.Add(type);
1112+
}
1113+
}
10941114
}
10951115
}
10961116

1097-
return allTypeMapping;
1117+
// We can throw away all lists and keep the dictionary as small as possible
1118+
Dictionary<Type, Type[]> map = new Dictionary<Type, Type[]>(temp.Count);
1119+
foreach (var kv in temp)
1120+
{
1121+
map[kv.Key] = kv.Value.ToArray();
1122+
}
1123+
1124+
return map;
10981125
}
10991126

11001127
/// <inheritdoc />

src/Microsoft.AspNet.OData.Shared/TypeHelper.cs

Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -416,44 +416,44 @@ internal static Type GetImplementedIEnumerableType(Type type)
416416
// This code is copied from DefaultHttpControllerTypeResolver.GetControllerTypes.
417417
internal static IEnumerable<Type> GetLoadedTypes(IWebApiAssembliesResolver assembliesResolver)
418418
{
419-
List<Type> result = new List<Type>();
420-
421-
if (assembliesResolver == null)
422-
{
423-
return result;
424-
}
425-
426-
// Go through all assemblies referenced by the application and search for types matching a predicate
427-
IEnumerable<Assembly> assemblies = assembliesResolver.Assemblies;
428-
foreach (Assembly assembly in assemblies)
419+
if (assembliesResolver != null)
429420
{
430-
Type[] exportedTypes = null;
431-
if (assembly == null || assembly.IsDynamic)
421+
// Go through all assemblies referenced by the application and search for types matching a predicate
422+
IEnumerable<Assembly> assemblies = assembliesResolver.Assemblies;
423+
foreach (Assembly assembly in assemblies)
432424
{
433-
// can't call GetTypes on a null (or dynamic?) assembly
434-
continue;
435-
}
425+
Type[] exportedTypes = null;
426+
if (assembly == null || assembly.IsDynamic)
427+
{
428+
// can't call GetTypes on a null (or dynamic?) assembly
429+
continue;
430+
}
436431

437-
try
438-
{
439-
exportedTypes = assembly.GetTypes();
440-
}
441-
catch (ReflectionTypeLoadException ex)
442-
{
443-
exportedTypes = ex.Types;
444-
}
445-
catch
446-
{
447-
continue;
448-
}
432+
try
433+
{
434+
exportedTypes = assembly.GetTypes();
435+
}
436+
catch (ReflectionTypeLoadException ex)
437+
{
438+
exportedTypes = ex.Types;
439+
}
440+
catch
441+
{
442+
continue;
443+
}
449444

450-
if (exportedTypes != null)
451-
{
452-
result.AddRange(exportedTypes.Where(t => t != null && TypeHelper.IsVisible(t)));
445+
if (exportedTypes != null)
446+
{
447+
foreach (Type t in exportedTypes)
448+
{
449+
if ((t != null) && (TypeHelper.IsVisible(t)))
450+
{
451+
yield return t;
452+
}
453+
}
454+
}
453455
}
454456
}
455-
456-
return result;
457457
}
458458

459459
internal static Type GetTaskInnerTypeOrSelf(Type type)

0 commit comments

Comments
 (0)