微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

c# – Reflect.Emit动态类型内存溢出

使用C#3.5我试图在运行时使用反射发射生成动态类型.我使用了Microsoft的 Dynamic Query Library样本来创建一个生成器.一切正常,我的问题是100个生成的类型使内存使用量大约25MB.这是一个完全不可接受的内存配置文件,最终我想支持在内存中生成数十万种类型.

内存分析显示内存显然被各种System.Reflection.Emit类型和方法占据,尽管我无法弄清楚为什么.我没有找到其他人谈论这个问题,所以我希望这个社区的某个人知道我做错了什么,或者这是预期的行为.

以下示例:

using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.Reflection.Emit;

namespace SmallRelfectExample
{
    class Program
    {
        static void Main(string[] args)
        {
            int typeCount = 100;
            int propCount = 100;
            Random rand = new Random();
            Type dynType = null;
            SlimClassFactory scf = new SlimClassFactory();
            for (int i = 0; i < typeCount; i++)
            {
                List<DynamicProperty> dpl = new List<DynamicProperty>(propCount);
                for (int j = 0; j < propCount; j++)
                {
                    dpl.Add(new DynamicProperty("Key" + rand.Next().ToString(),typeof(String)));
                }
                dynType = scf.CreateDynamicclass(dpl.ToArray(),i);
                //Optionally do something with the type here
            }
            Console.WriteLine("SmallRelfectExample: {0} Types generated.",typeCount);
            Console.ReadLine();
        }
    }
    public class SlimClassFactory
    {
        private readonly ModuleBuilder module;
        public SlimClassFactory()
        {
            AssemblyName name = new AssemblyName("Dynamicclasses");
            AssemblyBuilder assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(name,AssemblyBuilderAccess.Run);
            module = assembly.DefineDynamicModule("Module");

        }
        public Type CreateDynamicclass(DynamicProperty[] properties,int Id)
        {
            string typeName = "Dynamicclass" + Id.ToString();
            TypeBuilder tb = module.DefineType(typeName,TypeAttributes.Class |
                TypeAttributes.Public,typeof(Dynamicclass));
            FieldInfo[] fields = GenerateProperties(tb,properties);
            GenerateEquals(tb,fields);
            GenerateGetHashCode(tb,fields);
            Type result = tb.CreateType();
            return result;
        }
        static FieldInfo[] GenerateProperties(TypeBuilder tb,DynamicProperty[] properties)
        {
            FieldInfo[] fields = new FieldBuilder[properties.Length];
            for (int i = 0; i < properties.Length; i++)
            {
                DynamicProperty dp = properties[i];
                FieldBuilder fb = tb.DefineField("_" + dp.Name,dp.Type,FieldAttributes.Private);
                PropertyBuilder pb = tb.DefineProperty(dp.Name,PropertyAttributes.HasDefault,null);
                MethodBuilder mbGet = tb.DefineMethod("get_" + dp.Name,MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig,Type.EmptyTypes);
                ILGenerator genGet = mbGet.GetILGenerator();
                genGet.Emit(OpCodes.Ldarg_0);
                genGet.Emit(OpCodes.Ldfld,fb);
                genGet.Emit(OpCodes.Ret);
                MethodBuilder mbSet = tb.DefineMethod("set_" + dp.Name,null,new Type[] { dp.Type });
                ILGenerator genSet = mbSet.GetILGenerator();
                genSet.Emit(OpCodes.Ldarg_0);
                genSet.Emit(OpCodes.Ldarg_1);
                genSet.Emit(OpCodes.Stfld,fb);
                genSet.Emit(OpCodes.Ret);
                pb.Setgetmethod(mbGet);
                pb.SetSetMethod(mbSet);
                fields[i] = fb;
            }
            return fields;
        }
        static void GenerateEquals(TypeBuilder tb,FieldInfo[] fields)
        {
            MethodBuilder mb = tb.DefineMethod("Equals",MethodAttributes.Public | MethodAttributes.ReuseSlot |
                MethodAttributes.Virtual | MethodAttributes.HideBySig,typeof(bool),new Type[] { typeof(object) });
            ILGenerator gen = mb.GetILGenerator();
            LocalBuilder other = gen.DeclareLocal(tb);
            Label next = gen.DefineLabel();
            gen.Emit(OpCodes.Ldarg_1);
            gen.Emit(OpCodes.Isinst,tb);
            gen.Emit(OpCodes.Stloc,other);
            gen.Emit(OpCodes.Ldloc,other);
            gen.Emit(OpCodes.Brtrue_S,next);
            gen.Emit(OpCodes.Ldc_I4_0);
            gen.Emit(OpCodes.Ret);
            gen.MarkLabel(next);
            foreach (FieldInfo field in fields)
            {
                Type ft = field.FieldType;
                Type ct = typeof(EqualityComparer<>).MakeGenericType(ft);
                next = gen.DefineLabel();
                gen.EmitCall(OpCodes.Call,ct.getmethod("get_Default"),null);
                gen.Emit(OpCodes.Ldarg_0);
                gen.Emit(OpCodes.Ldfld,field);
                gen.Emit(OpCodes.Ldloc,other);
                gen.Emit(OpCodes.Ldfld,field);
                gen.EmitCall(OpCodes.Callvirt,ct.getmethod("Equals",new Type[] { ft,ft }),null);
                gen.Emit(OpCodes.Brtrue_S,next);
                gen.Emit(OpCodes.Ldc_I4_0);
                gen.Emit(OpCodes.Ret);
                gen.MarkLabel(next);
            }
            gen.Emit(OpCodes.Ldc_I4_1);
            gen.Emit(OpCodes.Ret);
        }
        static void GenerateGetHashCode(TypeBuilder tb,FieldInfo[] fields)
        {
            MethodBuilder mb = tb.DefineMethod("GetHashCode",typeof(int),Type.EmptyTypes);
            ILGenerator gen = mb.GetILGenerator();
            gen.Emit(OpCodes.Ldc_I4_0);
            foreach (FieldInfo field in fields)
            {
                Type ft = field.FieldType;
                Type ct = typeof(EqualityComparer<>).MakeGenericType(ft);
                gen.EmitCall(OpCodes.Call,ct.getmethod("GetHashCode",new Type[] { ft }),null);
                gen.Emit(OpCodes.Xor);
            }
            gen.Emit(OpCodes.Ret);
        }
    }
    public abstract class Dynamicclass
    {
        public override string ToString()
        {
            PropertyInfo[] props = GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);
            StringBuilder sb = new StringBuilder();
            sb.Append("{");
            for (int i = 0; i < props.Length; i++)
            {
                if (i > 0) sb.Append(",");
                sb.Append(props[i].Name);
                sb.Append("=");
                sb.Append(props[i].GetValue(this,null));
            }
            sb.Append("}");
            return sb.ToString();
        }
    }
    public class DynamicProperty
    {
        private readonly string name;
        private readonly Type type;

        public DynamicProperty(string name,Type type)
        {
            if (name == null) throw new ArgumentNullException("name");
            if (type == null) throw new ArgumentNullException("type");
            this.name = name;
            this.type = type;
        }

        public string Name
        {
            get { return name; }
        }

        public Type Type
        {
            get { return type; }
        }
    }
}

解决方法

不幸的是,在ModuleBuilder中有一个静态字段保存在内存中,这样永远不会得到GC.我不记得哪个字段和它现在包含什么,但是这可以从WinDbg中的SOS内部看到.

好消息是,.NET 4支持支持GC的动态程序集:)

原文地址:https://www.jb51.cc/csharp/94662.html

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐