Skip to content

Commit a57a49e

Browse files
committed
Fix ProxyFactory cache
- use a correct implementation of cache - reduce number of allocations of ProxyCacheEntry - reduce memory flow
1 parent 524d06a commit a57a49e

File tree

5 files changed

+58
-87
lines changed

5 files changed

+58
-87
lines changed

src/NHibernate/Proxy/DynamicProxy/HashSetExtensions.cs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,14 @@
22

33
namespace NHibernate.Proxy.DynamicProxy
44
{
5+
//TODO: Mark obsolete in v5.1
56
public static class HashSetExtensions
67
{
8+
//TODO: Mark obsolete in v5.1
79
public static HashSet<T> Merge<T>(this HashSet<T> source, IEnumerable<T> toMerge)
810
{
9-
foreach (T item in toMerge)
10-
{
11-
source.Add(item);
12-
}
11+
source.UnionWith(toMerge);
1312
return source;
1413
}
1514
}
16-
}
15+
}

src/NHibernate/Proxy/DynamicProxy/IProxyCache.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace NHibernate.Proxy.DynamicProxy
1313
{
14+
//TOOD: Mark as obsolete in v5.1
1415
public interface IProxyCache
1516
{
1617
bool Contains(System.Type baseType, params System.Type[] baseInterfaces);
@@ -20,4 +21,4 @@ public interface IProxyCache
2021

2122
void StoreProxyType(TypeInfo result, System.Type baseType, params System.Type[] baseInterfaces);
2223
}
23-
}
24+
}

src/NHibernate/Proxy/DynamicProxy/ProxyCache.cs

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,32 +6,27 @@
66

77
#endregion
88

9-
using System.Collections.Concurrent;
9+
using System;
1010
using System.Reflection;
1111

1212
namespace NHibernate.Proxy.DynamicProxy
1313
{
14+
//TOOD: Mark as obsolete in v5.1
1415
public class ProxyCache : IProxyCache
1516
{
16-
private static readonly ConcurrentDictionary<ProxyCacheEntry, TypeInfo> cache = new ConcurrentDictionary<ProxyCacheEntry, TypeInfo>();
17-
18-
#region IProxyCache Members
19-
2017
public bool Contains(System.Type baseType, params System.Type[] baseInterfaces)
2118
{
2219
if (baseType == null)
23-
{
2420
return false;
25-
}
2621

2722
var entry = new ProxyCacheEntry(baseType, baseInterfaces);
28-
return cache.ContainsKey(entry);
23+
return ProxyFactory._cache.ContainsKey(entry);
2924
}
3025

3126
public TypeInfo GetProxyType(System.Type baseType, params System.Type[] baseInterfaces)
3227
{
3328
var entry = new ProxyCacheEntry(baseType, baseInterfaces);
34-
return cache[entry];
29+
return ProxyFactory._cache[entry].Value;
3530
}
3631

3732
public bool TryGetProxyType(System.Type baseType, System.Type[] baseInterfaces, out TypeInfo proxyType)
@@ -42,15 +37,18 @@ public bool TryGetProxyType(System.Type baseType, System.Type[] baseInterfaces,
4237
return false;
4338

4439
var entry = new ProxyCacheEntry(baseType, baseInterfaces);
45-
return cache.TryGetValue(entry, out proxyType);
40+
41+
if (!ProxyFactory._cache.TryGetValue(entry, out var typeFactory))
42+
return false;
43+
44+
proxyType = typeFactory.Value;
45+
return true;
4646
}
4747

4848
public void StoreProxyType(TypeInfo result, System.Type baseType, params System.Type[] baseInterfaces)
4949
{
5050
var entry = new ProxyCacheEntry(baseType, baseInterfaces);
51-
cache[entry] = result;
51+
ProxyFactory._cache[entry] = new Lazy<TypeInfo>(() => result);
5252
}
53-
54-
#endregion
5553
}
5654
}

src/NHibernate/Proxy/DynamicProxy/ProxyCacheEntry.cs

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,23 +14,22 @@ namespace NHibernate.Proxy.DynamicProxy
1414
public class ProxyCacheEntry : IEquatable<ProxyCacheEntry>
1515
{
1616
private readonly int _hashCode;
17+
private readonly HashSet<System.Type> _uniqueInterfaces;
1718

1819
public ProxyCacheEntry(System.Type baseType, System.Type[] interfaces)
1920
{
20-
if (baseType == null)
21-
throw new ArgumentNullException(nameof(baseType));
22-
BaseType = baseType;
23-
_uniqueInterfaces = new HashSet<System.Type>(interfaces ?? new System.Type[0]);
21+
BaseType = baseType ?? throw new ArgumentNullException(nameof(baseType));
22+
23+
_uniqueInterfaces = new HashSet<System.Type>(interfaces ?? System.Type.EmptyTypes);
2424

2525
if (_uniqueInterfaces.Count == 0)
2626
{
2727
_hashCode = baseType.GetHashCode();
2828
return;
2929
}
30-
31-
var allTypes = new List<System.Type>(_uniqueInterfaces) { baseType };
32-
_hashCode = 59;
33-
foreach (System.Type type in allTypes)
30+
31+
_hashCode = 59 ^ baseType.GetHashCode();
32+
foreach (var type in _uniqueInterfaces)
3433
{
3534
// This simple implementation is nonsensitive to list order. If changing it for a sensitive one,
3635
// take care of ordering the list.
@@ -39,14 +38,12 @@ public ProxyCacheEntry(System.Type baseType, System.Type[] interfaces)
3938
}
4039

4140
public System.Type BaseType { get; }
42-
public IReadOnlyCollection<System.Type> Interfaces { get { return _uniqueInterfaces; } }
43-
44-
private HashSet<System.Type> _uniqueInterfaces;
41+
42+
public IReadOnlyCollection<System.Type> Interfaces => _uniqueInterfaces;
4543

4644
public override bool Equals(object obj)
4745
{
48-
var that = obj as ProxyCacheEntry;
49-
return Equals(that);
46+
return Equals(obj as ProxyCacheEntry);
5047
}
5148

5249
public bool Equals(ProxyCacheEntry other)
@@ -64,4 +61,4 @@ public bool Equals(ProxyCacheEntry other)
6461

6562
public override int GetHashCode() => _hashCode;
6663
}
67-
}
64+
}

src/NHibernate/Proxy/DynamicProxy/ProxyFactory.cs

Lines changed: 30 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#endregion
88

99
using System;
10+
using System.Collections.Concurrent;
1011
using System.Collections.Generic;
1112
using System.Linq;
1213
using System.Reflection;
@@ -19,6 +20,8 @@ namespace NHibernate.Proxy.DynamicProxy
1920
{
2021
public sealed class ProxyFactory
2122
{
23+
internal static readonly ConcurrentDictionary<ProxyCacheEntry, Lazy<TypeInfo>> _cache = new ConcurrentDictionary<ProxyCacheEntry, Lazy<TypeInfo>>();
24+
2225
private static readonly ConstructorInfo defaultBaseConstructor = typeof(object).GetConstructor(new System.Type[0]);
2326

2427
private static readonly MethodInfo getValue = ReflectHelper.GetMethod<SerializationInfo>(
@@ -39,58 +42,40 @@ public ProxyFactory(IProxyMethodBuilder proxyMethodBuilder)
3942

4043
public ProxyFactory(IProxyMethodBuilder proxyMethodBuilder, IProxyAssemblyBuilder proxyAssemblyBuilder)
4144
{
42-
if (proxyMethodBuilder == null)
43-
{
44-
throw new ArgumentNullException("proxyMethodBuilder");
45-
}
46-
ProxyMethodBuilder = proxyMethodBuilder;
45+
ProxyMethodBuilder = proxyMethodBuilder ?? throw new ArgumentNullException(nameof(proxyMethodBuilder));
4746
ProxyAssemblyBuilder = proxyAssemblyBuilder;
4847
Cache = new ProxyCache();
4948
}
5049

51-
public IProxyCache Cache { get; private set; }
50+
public IProxyCache Cache { get; }
5251

53-
public IProxyMethodBuilder ProxyMethodBuilder { get; private set; }
52+
public IProxyMethodBuilder ProxyMethodBuilder { get; }
5453

55-
public IProxyAssemblyBuilder ProxyAssemblyBuilder { get; private set; }
54+
public IProxyAssemblyBuilder ProxyAssemblyBuilder { get; }
5655

5756
public object CreateProxy(System.Type instanceType, IInterceptor interceptor, params System.Type[] baseInterfaces)
5857
{
5958
System.Type proxyType = CreateProxyType(instanceType, baseInterfaces);
6059
object result = Activator.CreateInstance(proxyType);
6160
var proxy = (IProxy) result;
6261
proxy.Interceptor = interceptor;
63-
6462
return result;
6563
}
6664

6765
public System.Type CreateProxyType(System.Type baseType, params System.Type[] interfaces)
6866
{
69-
System.Type[] baseInterfaces = ReferenceEquals(null, interfaces) ? new System.Type[0] : interfaces.Where(t => t != null).ToArray();
70-
71-
TypeInfo proxyTypeInfo;
72-
73-
// Reuse the previous results, if possible, Fast path without locking.
74-
if (Cache.TryGetProxyType(baseType, baseInterfaces, out proxyTypeInfo))
75-
return proxyTypeInfo;
67+
if (baseType == null) throw new ArgumentNullException(nameof(baseType));
7668

77-
lock (Cache)
78-
{
79-
// Recheck in case we got interrupted.
80-
if (!Cache.TryGetProxyType(baseType, baseInterfaces, out proxyTypeInfo))
81-
{
82-
proxyTypeInfo = CreateUncachedProxyType(baseType, baseInterfaces);
69+
var baseInterfaces = ReferenceEquals(null, interfaces) ? System.Type.EmptyTypes : interfaces.Where(t => t != null).ToArray();
8370

84-
// Cache the proxy type
85-
if (proxyTypeInfo != null && Cache != null)
86-
Cache.StoreProxyType(proxyTypeInfo, baseType, baseInterfaces);
87-
}
71+
var typeFactory = _cache.GetOrAdd(
72+
new ProxyCacheEntry(baseType, baseInterfaces),
73+
k => new Lazy<TypeInfo>(() => CreateUncachedProxyType(k.BaseType, k.Interfaces)));
8874

89-
return proxyTypeInfo;
90-
}
75+
return typeFactory.Value;
9176
}
9277

93-
private TypeInfo CreateUncachedProxyType(System.Type baseType, System.Type[] baseInterfaces)
78+
private TypeInfo CreateUncachedProxyType(System.Type baseType, IReadOnlyCollection<System.Type> baseInterfaces)
9479
{
9580
AppDomain currentDomain = AppDomain.CurrentDomain;
9681
string typeName = string.Format("{0}Proxy", baseType.Name);
@@ -104,8 +89,7 @@ private TypeInfo CreateUncachedProxyType(System.Type baseType, System.Type[] bas
10489
TypeAttributes typeAttributes = TypeAttributes.AutoClass | TypeAttributes.Class |
10590
TypeAttributes.Public | TypeAttributes.BeforeFieldInit;
10691

107-
var interfaces = new HashSet<System.Type>();
108-
interfaces.Merge(baseInterfaces);
92+
var interfaces = new HashSet<System.Type>(baseInterfaces);
10993

11094
// Use the proxy dummy as the base type
11195
// since we're not inheriting from any class type
@@ -117,11 +101,8 @@ private TypeInfo CreateUncachedProxyType(System.Type baseType, System.Type[] bas
117101
}
118102

119103
// Add any inherited interfaces
120-
System.Type[] computedInterfaces = interfaces.ToArray();
121-
foreach (System.Type interfaceType in computedInterfaces)
122-
{
123-
interfaces.Merge(GetInterfaces(interfaceType));
124-
}
104+
var list = interfaces.SelectMany(GetAllInterfaces).ToArray();
105+
interfaces.UnionWith(list);
125106

126107
// Add the ISerializable interface so that it can be implemented
127108
interfaces.Add(typeof (ISerializable));
@@ -151,19 +132,13 @@ private TypeInfo CreateUncachedProxyType(System.Type baseType, System.Type[] bas
151132
return proxyType;
152133
}
153134

154-
private IEnumerable<System.Type> GetInterfaces(System.Type currentType)
135+
private static IEnumerable<System.Type> GetAllInterfaces(System.Type currentType)
155136
{
156-
return GetAllInterfaces(currentType);
157-
}
158-
159-
private IEnumerable<System.Type> GetAllInterfaces(System.Type currentType)
160-
{
161-
System.Type[] interfaces = currentType.GetInterfaces();
162-
163-
foreach (System.Type current in interfaces)
137+
var interfaces = currentType.GetInterfaces();
138+
foreach (var current in interfaces)
164139
{
165140
yield return current;
166-
foreach (System.Type @interface in GetAllInterfaces(current))
141+
foreach (var @interface in GetAllInterfaces(current))
167142
{
168143
yield return @interface;
169144
}
@@ -173,10 +148,11 @@ private TypeInfo CreateUncachedProxyType(System.Type baseType, System.Type[] bas
173148
private IEnumerable<MethodInfo> GetProxiableMethods(System.Type type, IEnumerable<System.Type> interfaces)
174149
{
175150
const BindingFlags candidateMethodsBindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
176-
return
151+
return
177152
type.GetMethods(candidateMethodsBindingFlags)
178-
.Where(method=> method.IsProxiable())
179-
.Concat(interfaces.SelectMany(interfaceType => interfaceType.GetMethods())).Distinct();
153+
.Where(method => method.IsProxiable())
154+
.Concat(interfaces.SelectMany(interfaceType => interfaceType.GetMethods()))
155+
.Distinct();
180156
}
181157

182158
private static ConstructorBuilder DefineConstructor(TypeBuilder typeBuilder, System.Type parentType)
@@ -206,7 +182,7 @@ private static ConstructorBuilder DefineConstructor(TypeBuilder typeBuilder, Sys
206182
return constructor;
207183
}
208184

209-
private static void ImplementGetObjectData(System.Type baseType, System.Type[] baseInterfaces, TypeBuilder typeBuilder, FieldInfo interceptorField)
185+
private static void ImplementGetObjectData(System.Type baseType, IReadOnlyCollection<System.Type> baseInterfaces, TypeBuilder typeBuilder, FieldInfo interceptorField)
210186
{
211187
const MethodAttributes attributes = MethodAttributes.Public | MethodAttributes.HideBySig |
212188
MethodAttributes.Virtual;
@@ -236,7 +212,7 @@ private static void ImplementGetObjectData(System.Type baseType, System.Type[] b
236212
IL.Emit(OpCodes.Ldstr, baseType.AssemblyQualifiedName);
237213
IL.Emit(OpCodes.Callvirt, addValue);
238214

239-
int baseInterfaceCount = baseInterfaces.Length;
215+
int baseInterfaceCount = baseInterfaces.Count;
240216

241217
// Save the number of base interfaces
242218
IL.Emit(OpCodes.Ldarg_1);
@@ -249,7 +225,7 @@ private static void ImplementGetObjectData(System.Type baseType, System.Type[] b
249225
foreach (System.Type baseInterface in baseInterfaces)
250226
{
251227
IL.Emit(OpCodes.Ldarg_1);
252-
IL.Emit(OpCodes.Ldstr, string.Format("__baseInterface{0}", index++));
228+
IL.Emit(OpCodes.Ldstr, String.Format("__baseInterface{0}", index++));
253229
IL.Emit(OpCodes.Ldstr, baseInterface.AssemblyQualifiedName);
254230
IL.Emit(OpCodes.Callvirt, addValue);
255231
}
@@ -294,7 +270,7 @@ private static void DefineSerializationConstructor(TypeBuilder typeBuilder, Fiel
294270
IL.Emit(OpCodes.Ret);
295271
}
296272

297-
private static void AddSerializationSupport(System.Type baseType, System.Type[] baseInterfaces, TypeBuilder typeBuilder, FieldInfo interceptorField, ConstructorBuilder defaultConstructor)
273+
private static void AddSerializationSupport(System.Type baseType, IReadOnlyCollection<System.Type> baseInterfaces, TypeBuilder typeBuilder, FieldInfo interceptorField, ConstructorBuilder defaultConstructor)
298274
{
299275
ConstructorInfo serializableConstructor = typeof(SerializableAttribute).GetConstructor(new System.Type[0]);
300276
var customAttributeBuilder = new CustomAttributeBuilder(serializableConstructor, new object[0]);
@@ -304,4 +280,4 @@ private static void AddSerializationSupport(System.Type baseType, System.Type[]
304280
ImplementGetObjectData(baseType, baseInterfaces, typeBuilder, interceptorField);
305281
}
306282
}
307-
}
283+
}

0 commit comments

Comments
 (0)