我正在生成一个包含大量switch语句的方法。我注意到在ildasm中.maxstack值非常高。我的理解是.maxstack是给定方法的最大堆栈深度?我找不到很多关于网上的信息。
在前两个示例中,最大堆栈大小超过4KB。在最后一个示例中,最大堆栈大小为509,但实际最大深度似乎为10.为什么该值如此之高?这只是jit的暗示?有这么高的.maxstack会有什么影响吗?我在互联网上读到的关于它的最大深度是不正确的吗?
似乎我的第三个例子中有一个错误。我没有peverify或测试,并在加载参数后有一个额外的推动。在我修复了maxstack为9.对于前两个示例,使用reflection.emit,最大堆栈大小保持在4K以上。使用C#编译器,switch方法是9,而不是4502 with reflection emit。
根据答案,似乎他们为每个基本块添加最大深度作为总结,而C#编译器更准确地计算它。我仍然对这种高价值的影响感到好奇。
class Program
{
static void Main(string[] args)
{
Foo();
Bar();
FooBar();
}
static void Foo()
{
// Increasing this value will increase the stack depth by the number of labels.
int caseStackDepth = 8;
string name = Path.ChangeExtension("foo", ".dll");
AssemblyName assemblyName = new AssemblyName("foo");
AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave);
ModuleBuilder module = assemblyBuilder.DefineDynamicModule(assemblyName.Name, name);
TypeBuilder type = module.DefineType("boo", System.Reflection.TypeAttributes.Class | System.Reflection.TypeAttributes.Public);
MethodBuilder method = type.DefineMethod(
"bar",
System.Reflection.MethodAttributes.Public | System.Reflection.MethodAttributes.Static,
typeof(int),
new [] { typeof(int) });
ILGenerator generator = method.GetILGenerator();
LocalBuilder result = generator.DeclareLocal(typeof(int));
Label[] labels = new Label[500];
for (int index = 0; index < labels.Length; index++)
{
labels[index] = generator.DefineLabel();
}
Label end = generator.DefineLabel();
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Switch, labels);
generator.Emit(OpCodes.Br, end);
for (int index = 0; index < labels.Length; index++)
{
generator.MarkLabel(labels[index]);
generator.Emit(OpCodes.Ldc_I4, index);
// Simulate stack depth.
for (int depth = 0; depth < caseStackDepth; depth++)
{
generator.Emit(OpCodes.Dup);
}
for (int depth = 0; depth < caseStackDepth; depth++)
{
generator.Emit(OpCodes.Add);
}
generator.Emit(OpCodes.Stloc, result);
generator.Emit(OpCodes.Br, end);
}
generator.MarkLabel(end);
generator.Emit(OpCodes.Ldloc, result);
generator.Emit(OpCodes.Ret);
type.CreateType();
assemblyBuilder.Save("foo.dll");
}
static void Bar()
{
// Increasing this value will increase the stack depth by the number of labels.
int caseStackDepth = 8;
string name = Path.ChangeExtension("bar", ".dll");
AssemblyName assemblyName = new AssemblyName("bar");
AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave);
ModuleBuilder module = assemblyBuilder.DefineDynamicModule(assemblyName.Name, name);
TypeBuilder type = module.DefineType("boo", System.Reflection.TypeAttributes.Class | System.Reflection.TypeAttributes.Public);
MethodBuilder method = type.DefineMethod(
"bar",
System.Reflection.MethodAttributes.Public | System.Reflection.MethodAttributes.Static,
typeof(int),
new[] { typeof(int) });
ILGenerator generator = method.GetILGenerator();
LocalBuilder result = generator.DeclareLocal(typeof(int));
Label end = generator.DefineLabel();
for (int index = 0; index < 500; index++)
{
Label equal = generator.DefineLabel();
Label notEqual = generator.DefineLabel();
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldc_I4, index);
generator.Emit(OpCodes.Beq, equal);
generator.Emit(OpCodes.Br, notEqual);
generator.MarkLabel(equal);
generator.Emit(OpCodes.Ldc_I4, index);
// Simulate stack depth.
for (int depth = 0; depth < caseStackDepth; depth++)
{
generator.Emit(OpCodes.Dup);
}
for (int depth = 0; depth < caseStackDepth; depth++)
{
generator.Emit(OpCodes.Add);
}
generator.Emit(OpCodes.Stloc, result);
generator.Emit(OpCodes.Br, end);
generator.MarkLabel(notEqual);
}
generator.MarkLabel(end);
generator.Emit(OpCodes.Ldloc, result);
generator.Emit(OpCodes.Ret);
type.CreateType();
assemblyBuilder.Save("bar.dll");
}
static void FooBar()
{
// Increasing this value will increase the stack depth by the number of labels.
int caseStackDepth = 8;
string name = Path.ChangeExtension("foobar", ".dll");
AssemblyName assemblyName = new AssemblyName("foobar");
AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave);
ModuleBuilder module = assemblyBuilder.DefineDynamicModule(assemblyName.Name, name);
TypeBuilder type = module.DefineType("boo", System.Reflection.TypeAttributes.Class | System.Reflection.TypeAttributes.Public);
MethodBuilder method = type.DefineMethod(
"bar",
System.Reflection.MethodAttributes.Public | System.Reflection.MethodAttributes.Static,
typeof(int),
new[] { typeof(int) });
ILGenerator generator = method.GetILGenerator();
LocalBuilder result = generator.DeclareLocal(typeof(int));
for (int index = 0; index < 500; index++)
{
generator.Emit(OpCodes.Ldarg_0);
//错误,这导致堆栈大小非常高。 // generator.Emit(OpCodes.Ldc_I4,index);
// Simulate stack depth.
for (int depth = 0; depth < caseStackDepth; depth++)
{
generator.Emit(OpCodes.Dup);
}
for (int depth = 0; depth < caseStackDepth; depth++)
{
generator.Emit(OpCodes.Add);
}
generator.Emit(OpCodes.Stloc, result);
}
generator.Emit(OpCodes.Ldloc, result);
generator.Emit(OpCodes.Ret);
type.CreateType();
assemblyBuilder.Save("foobar.dll");
}
}
答案 0 :(得分:2)
查看Reference Source,我们可以找到跟踪所需堆栈大小的方法,称为UpdateStackSize。内部的评论似乎在说明:
// If the current instruction signifies end of a basic, which basically
// means an unconditional branch, add m_maxMidStack to m_maxStackSize.
// m_maxStackSize will eventually be the sum of the stack requirements for
// each basic block.
也就是说,没有尝试执行任何控制流分析。每个基本块的最大所需堆栈大小将加在一起。
我不知道这个尺寸的任何重大影响都异常高。
答案 1 :(得分:2)