использование дескрипторов привязки
для снижения потребления памяти процессом
Во многих приложениях требуется привязка к нескольким типам (то есть объектам
Type
) или их членам (объектам, производным от
MemberInfo
), а эти объекты со-
храняются в определенной коллекции. Позже приложение ищет нужный объект в
коллекции и вызывает его. Это разумное решение, но есть одна загвоздка: объекты
Type
и объекты, производные от
MemberInfo
, занимают много места в памяти. По-
этому если в приложении много таких объектов и к ним надо обращаться часто,
объем потребляемой памяти резко возрастает, что отрицательно сказывается на
производительности.
Внутренние механизмы CLR поддерживают более компактную форму хранения
этой информации. CLR создает такие объекты в приложениях лишь для того, чтобы
упростить работу программиста. Самой среде CLR для работы эти большие объекты
664
Глава.23 .Загрузка.сборок.и.отражение
не нужны. В приложениях, в которых сохраняется и кэшируется много объектов
Type
и объектов-потомков
MemberInfo
, можно сократить потребление памяти, если
использовать не объекты, а описатели времени выполнения. В FCL определены
три типа таких описателей (все в пространстве имен
System
):
RuntimeTypeHandle
,
RuntimeFieldHandle
и
RuntimeMethodHandle
. Все они — значимые типы с единствен-
ным полем
IntPtr
; за счет чего расходуют очень мало ресурсов (то есть памяти).
Поле
IntPtr
представляет собой дескриптор, ссылающийся на тип, поле или метод
в куче загрузчика домена приложений. Так что теперь нам достаточно научиться
просто и эффективно преобразовывать «тяжелые» объекты
Type
и
MemberInfo
в «легкие» дескрипторы времени выполнения, и наоборот. Это не сложно, если
задействовать перечисленные далее методы и свойства.
Чтобы преобразовать объект
Type
в
RuntimeTypeHandle
, вызовите статический
метод
GetTypeHandle
объекта
Type
, передав ему ссылку на объект
Type
.
Чтобы преобразовать
RuntimeTypeHandle
в объект
Type
, вызовите статический
метод
GetTypeFromHandle
объекта
Type
, передав ему
RuntimeTypeHandle
.
Чтобы преобразовать объект
FieldInfo
в
RuntimeFieldHandle
, запросите эк-
земплярное неизменяемое свойство
FieldHandle
объекта
FieldInfo
.
Чтобы преобразовать
RuntimeTypeHandle
в объект
FieldInfo
, вызовите стати-
ческий метод
GetTypeFromHandle
объекта
FieldInfo
.
Чтобы преобразовать объект
MethodInfo
в
RuntimeMethodHandle
, запросите
экземплярное неизменяемое свойство
MethodHandle
объекта
MethodInfo
.
Чтобы преобразовать
RuntimeTypeHandle
в объект
MethodInfo
, вызовите ста-
тический метод
GetMethodFromHandle
объекта
MethodInfo
.
В приведенной далее программе создается много объектов
MethodInfo
, которые
преобразуются в экземпляры
RuntimeMethodHandle
, а затем выводится информация
о разнице в объеме потребляемой памяти:
using System;
using System.Reflection;
using System.Collections.Generic;
public sealed class Program {
private const BindingFlags c_bf = BindingFlags.FlattenHierarchy |
BindingFlags.Instance |
BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
public static void Main() {
// Выводим размер кучи до отражения
Show("Before doing anything");
// Создаем кэш объектов MethodInfo для всех методов из MSCorlib.dll
List methodInfos = new List();
foreach (Type t in typeof(Object).Assembly.GetExportedTypes()) {
665
Нахождение.членов.типа.путем.отражения
// Игнорируем обобщенные типы
if (t.IsGenericTypeDefinition) continue;
MethodBase[] mb = t.GetMethods(c_bf);
methodInfos.AddRange(mb);
}
// Выводим количество методов и размер кучи после привязки всех методов
Console.WriteLine("# of methods={0:N0}", methodInfos.Count);
Show("After building cache of MethodInfo objects");
// Создаем кэш дескрипторов RuntimeMethodHandles
// для всех объектов MethodInfo
List methodHandles =
methodInfos.ConvertAll(mb => mb.MethodHandle);
Show("Holding MethodInfo and RuntimeMethodHandle cache");
GC.KeepAlive(methodInfos); // Запрещаем уборку мусора в кэше
methodInfos = null; // Разрешаем уборку мусора в кэше
Show("After freeing MethodInfo objects");
methodInfos = methodHandles.ConvertAll(
rmh=> MethodBase.GetMethodFromHandle(rmh));
Show("Size of heap after re-creating MethodInfo objects");
GC.KeepAlive(methodHandles); // Запрещаем уборку мусора в кэше
GC.KeepAlive(methodInfos); // Запрещаем уборку мусора в кэше
methodHandles = null; // Разрешаем уборку мусора в кэше
methodInfos = null; // Разрешаем уборку мусора в кэше
Show("After freeing MethodInfos and RuntimeMethodHandles");
}
}
При построении и выполнении этой программы выводится следующий ре-
зультат:
Heap size= 85,000 - Before doing any
thing
# of methods=48,467
Heap size= 7,065,632 - After building cache of MethodInfo objects
Heap size= 7,453,496 - Holding MethodInfo and RuntimeMethodHandle cache
Heap size= 6,732,704 - After freeing MethodInfo objects
Heap size= 7,372,704 - Size of heap after re-creating MethodInfo objects
Heap size= 192,232 - After freeing
MethodInfos and RuntimeMethodHandles
Do'stlaringiz bilan baham: |