C# 为什么堆或堆栈中不存在结构实例

C# 为什么堆或堆栈中不存在结构实例,c#,.net,memory-management,windbg,sos,C#,.net,Memory Management,Windbg,Sos,我有以下代码 public class ClassTest { public int Id { get; set; } public int OtherId { get; set; } } public struct StructTest { public int Id { get; set; } public int OtherId { get; set; } } static void

我有以下代码

public class ClassTest
    {
        public int Id { get; set; }
        public int OtherId { get; set; }
    }

    public struct StructTest
    {
        public int Id { get; set; }
        public int OtherId { get; set; }
    }

static void Main(string[] args)
        {
            ClassTest c = new ClassTest() { Id = 1, OtherId = 2 };
            StructTest s = new StructTest() { Id = 51, OtherId = 52 };
            Console.WriteLine("Attach the debugger now.");
            Console.ReadKey();
            GC.KeepAlive(s);

            Console.WriteLine("Done");
        }
我在应用程序等待ReadKey调用时附加windbg。然后我运行以下命令

0:004> !DumpHeap -stat
Statistics:
              MT    Count    TotalSize Class Name
000007fee1419b10        1           24 System.IntPtr
000007fee1416090        1           24 System.Collections.Generic.GenericEqualityComparer`1[[System.String, mscorlib]]
000007fee140a1a8        1           24 System.Reflection.Missing
000007fee140a060        1           24 System.__Filters
000007fee14090a0        1           24 System.Reflection.__Filters
000007fee1408ab0        1           24 System.Attribute[]
000007fee1408978        1           24 System.Collections.Generic.ObjectEqualityComparer`1[[System.RuntimeType, mscorlib]]
000007fee1408058        1           24 System.Security.HostSecurityManager
000007fee14074a8        1           24 System.Collections.Generic.ObjectEqualityComparer`1[[System.Type, mscorlib]]
000007fe82c85bd8        1           24 ConsoleApplication1.ClassTest
000007fee145c360        1           32 Microsoft.Win32.SafeHandles.SafeFileMappingHandle
000007fee145c2d0        1           32 Microsoft.Win32.SafeHandles.SafeViewOfFileHandle
000007fee1415ba8        1           32 System.Version
000007fee14155c0        1           32 Microsoft.Win32.SafeHandles.SafeFileHandle
000007fee140d548        1           32 System.Reflection.RuntimePropertyInfo[]
000007fee1408240        1           32 System.Runtime.Versioning.TargetFrameworkAttribute
000007fee1407fe0        1           32 System.Security.Policy.Evidence+EvidenceLockHolder
000007fee1407188        1           32 System.Security.Policy.AssemblyEvidenceFactory
000007fee1407040        1           32 Microsoft.Win32.SafeHandles.SafePEFileHandle
000007fee140a570        1           35 System.Boolean[]
000007fee145c558        1           40 Microsoft.Win32.Win32Native+InputRecord
000007fee145c1c8        1           40 System.Text.InternalEncoderBestFitFallback
000007fee145bce0        1           40 System.IO.Stream+NullStream
000007fee140d6e8        1           40 System.Reflection.CerHashtable`2+Table[[System.String, mscorlib],[System.Reflection.RuntimePropertyInfo[], mscorlib]]
000007fee145cad0        1           48 System.Text.EncoderNLS
000007fee145c7b8        1           48 System.IO.TextWriter+SyncTextWriter
000007fee145c258        1           48 System.Text.InternalDecoderBestFitFallback
000007fee1416ee0        1           48 System.Text.UTF8Encoding
000007fee1411db0        1           48 System.SharedStatics
000007fee140b030        2           48 System.Reflection.ParameterInfo[]
000007fee1408140        1           48 System.AppDomainManager
000007fee145c5d8        1           56 System.IO.__ConsoleStream
000007fee1415058        1           56 System.Text.UnicodeEncoding
000007fee140d4d8        1           56 System.RuntimeType+RuntimeTypeCache+MemberInfoCache`1[[System.Reflection.RuntimePropertyInfo, mscorlib]]
000007fee140c4b8        1           56 System.RuntimeType+RuntimeTypeCache+MemberInfoCache`1[[System.Reflection.RuntimeMethodInfo, mscorlib]]
000007fee1415460        2           64 System.Text.DecoderReplacementFallback
000007fee14153d0        2           64 System.Text.EncoderReplacementFallback
000007fee1412df8        1           64 System.Security.PermissionSet
000007fee1407f60        1           64 System.Threading.ReaderWriterLock
000007fee14070e8        1           64 System.Security.Policy.PEFileEvidenceFactory
000007fee1413e98        3           72 System.Int32
000007fee1412c60        1           72 System.Security.Policy.Evidence
000007fee1415668        1           80 System.Collections.Hashtable
000007fee140d758        1           80 System.Reflection.RuntimePropertyInfo[][]
000007fee0da09e0        1           80 System.Collections.Generic.Dictionary`2[[System.String, mscorlib],[System.Globalization.CultureData, mscorlib]]
000007fee0da0030        1           80 System.Collections.Generic.Dictionary`2[[System.RuntimeType, mscorlib],[System.RuntimeType, mscorlib]]
000007fee0d9fee8        1           80 System.Collections.Generic.Dictionary`2[[System.Type, mscorlib],[System.Security.Policy.EvidenceTypeDescriptor, mscorlib]]
000007fee1416268        1           96 System.Collections.Generic.Dictionary`2+Entry[[System.String, mscorlib],[System.Globalization.CultureData, mscorlib]][]
000007fee1415710        1           96 System.Collections.Hashtable+bucket[]
000007fee1412bb0        1           96 System.Threading.Thread
000007fee140e0e0        1           96 System.RuntimeMethodInfoStub
000007fee1409438        4           96 System.UInt16
000007fee140d2b0        1          104 System.Reflection.RuntimePropertyInfo
000007fee1409208        1          104 System.IO.UnmanagedMemoryStream
000007fee1416958        1          112 System.IO.StreamWriter
000007fee1414a98        2          112 System.Reflection.RuntimeAssembly
000007fee140c528        3          120 System.Reflection.RuntimeMethodInfo[]
000007fee145bfc8        1          128 System.Text.SBCSCodePageEncoding
000007fee14125d0        1          128 System.AppDomainSetup
000007fee1409140        2          128 System.Reflection.TypeFilter
000007fee1408b28        2          128 System.Reflection.RuntimeModule
000007fee1407530        2          128 System.Type[]
000007fee1415dc0        3          144 System.Text.StringBuilder
000007fee1415d50        1          160 System.Globalization.CalendarData
000007fee1411bc0        1          160 System.ExecutionEngineException
000007fee1411b48        1          160 System.StackOverflowException
000007fee1411ad0        1          160 System.OutOfMemoryException
000007fee14118e8        1          160 System.Exception
000007fee1409ba8        1          160 System.RuntimeType+RuntimeTypeCache
000007fee1411c98        7          168 System.Object
000007fee1406fe0        2          168 System.Runtime.Versioning.TargetFrameworkAttribute[]
000007fee140a118        3          192 System.Reflection.MemberFilter
000007fee1408340        4          192 System.RuntimeType[]
000007fee1415cd8        1          208 System.Globalization.CalendarData[]
000007fee14164b0        1          216 System.Globalization.NumberFormatInfo
000007fee1411e70        1          216 System.AppDomain
000000000053bd50        8          216      Free
000007fee140c1b0        2          224 System.Reflection.RuntimeMethodInfo
000007fee140ae30        3          240 System.Signature
000007fee14093d0        1          281 System.Byte[]
000007fee1408840        1          288 System.Collections.Generic.Dictionary`2+Entry[[System.RuntimeType, mscorlib],[System.RuntimeType, mscorlib]][]
000007fee1411c38        2          320 System.Threading.ThreadAbortException
000007fee1408d70        1          360 System.Reflection.CustomAttributeRecord[]
000007fee14157f0        3          384 System.Globalization.CultureInfo
000007fee1407e28        3          720 System.Collections.Generic.Dictionary`2+Entry[[System.Type, mscorlib],[System.Security.Policy.EvidenceTypeDescriptor, mscorlib]][]
000007fee1413e30       12          764 System.Int32[]
000007fee1412860        8          932 System.Char[]
000007fee1412aa8       19         1296 System.String[]
000007fee1415b50        3         1608 System.Globalization.CultureData
000007fee1413698       58         3248 System.RuntimeType
000007fee14116b8      176         8394 System.String
000007fee1411d30        8        35280 System.Object[]
Total 412 objects
0:004> ~0s
ntdll!ZwRequestWaitReplyPort+0xa:
00000000`7708bf5a c3              ret
0:000> !CLRStack
OS Thread Id: 0x453c (0)
        Child SP               IP Call Site
000000000030e998 000000007708bf5a [InlinedCallFrame: 000000000030e998] Microsoft.Win32.Win32Native.ReadConsoleInput(IntPtr, InputRecord ByRef, Int32, Int32 ByRef)
000000000030e998 000007fee19b9781 [InlinedCallFrame: 000000000030e998] Microsoft.Win32.Win32Native.ReadConsoleInput(IntPtr, InputRecord ByRef, Int32, Int32 ByRef)
000000000030e960 000007fee19b9781 *** WARNING: Unable to verify checksum for C:\windows\assembly\NativeImages_v4.0.30319_64\mscorlib\f89061884b75dab0e3967d7221e5290d\mscorlib.ni.dll
DomainNeutralILStubClass.IL_STUB_PInvoke(IntPtr, InputRecord ByRef, Int32, Int32 ByRef)
000000000030ea70 000007fee1a86e26 System.Console.ReadKey(Boolean)
000000000030eb60 000007fe82d90547 *** WARNING: Unable to verify checksum for c:\users\james\documents\visual studio 2015\Projects\ConsoleApplication1\ConsoleApplication1\bin\Debug\ConsoleApplication1.exe
ConsoleApplication1.Program.Main(System.String[]) [c:\users\james\documents\visual studio 2015\Projects\ConsoleApplication1\ConsoleApplication1\Program.cs @ 16]
000000000030ee30 000007fee2386a53 [GCFrame: 000000000030ee30] 
0:000> !dso
OS Thread Id: 0x453c (0)
RSP/REG          Object           Name
000000000030EAB0 0000000002387670 System.Object
000000000030EAB8 0000000002384518 System.String    Attach the debugger now.
000000000030EB40 0000000002384500 System.String[]
000000000030EBA0 0000000002384590 ConsoleApplication1.ClassTest
000000000030EBA8 0000000002384590 ConsoleApplication1.ClassTest
000000000030EC00 0000000002384500 System.String[]
000000000030ECE8 0000000002384500 System.String[]
000000000030EDA8 0000000002384500 System.String[]
000000000030EDB0 0000000002382518 System.RuntimeType
000000000030EDF8 0000000002382e50 System.RuntimeType
000000000030EF78 0000000002384500 System.String[]
000000000030EFA0 0000000002381658 System.AppDomain
000000000030F088 0000000002381658 System.AppDomain
000000000030F0E0 0000000002383bb8 System.String    .NETFramework,Version=v4.0
000000000030F258 0000000002381658 System.AppDomain
000000000030F518 0000000002381440 System.SharedStatics
我的问题是,为什么在堆或线程堆栈对象的任何位置都看不到StructTest的任何实例?

您的案例 您的对象显然不在堆上。您可以使用
转储堆栈对象!dso
,但这也不会显示它。原因是JIT编译器编译它时只使用寄存器:

0:000> !clrstack
OS Thread Id: 0x1de4 (0)
Child SP       IP Call Site
003defb0 74cc7f8e [InlinedCallFrame: 003defb0] 
003defac 7189d80b DomainNeutralILStubClass.IL_STUB_PInvoke(IntPtr, InputRecord ByRef, Int32, Int32 ByRef)
003defb0 7193616a [InlinedCallFrame: 003defb0] Microsoft.Win32.Win32Native.ReadConsoleInput(IntPtr, InputRecord ByRef, Int32, Int32 ByRef)
003df038 7193616a System.Console.ReadKey(Boolean)
003df0c0 001a048a StructNotInHeap.Program.Main(System.String[]) [F:\Debugging\Source\StackOverflow\StructNotInHeap\Program.cs @ 22]
003df248 722fea96 [GCFrame: 003df248] 

0:000> !U /d 001a048a
Normal JIT generated code
StructNotInHeap.Program.Main(System.String[])
Begin 001a0448, size 70
[...]

F:\Debugging\Source\StackOverflow\StructNotInHeap\Program.cs @ 22:
001a0469 b833000000      mov     eax,33h
001a046e 8d5001          lea     edx,[eax+1]
当然,第22行是

StructTest s = new StructTest() { Id = 51, OtherId = 52 };
mov eax,33h
将0x33(或51位小数)移动到eax寄存器,从而初始化
Id
属性
lea-edx,[eax+1]
是一种将eax+1(或52位十进制数)存储到edx寄存器的智能方法,因此
OtherId
被初始化。它是智能的,因为它使用寻址管道而不是CPU的算法单元

堆上的结构 再深入一点,它似乎是
!dumpheap
从不列出非固定值类型,即使它们与对象一起在堆上分配。你需要
!DumpVC
命令以查看作为堆上对象一部分的值类型

如果它们被装箱,你可以在堆上找到它们。将您的命令用于此程序:

class Program
{
    public class MyClass
    {
        public StructTest MyStruct;
    }

    public struct StructTest
    {
        public int Id { get; set; }
        public int OtherId { get; set; }
    }

    private static void Main()
    {
        var c = new MyClass();
        c.MyStruct.Id = 51;
        c.MyStruct.OtherId = 52;
        object boxed = c.MyStruct;
        Console.WriteLine("Attach the debugger now.");
        Console.ReadKey();
        Console.WriteLine(c.MyStruct.Id.ToString() +  boxed);
    }
}
在这种装箱的情况下,您还可以在堆栈上找到对它的引用(
!dso

堆栈上的结构 在发布版本中,事情得到了极大的优化。在64字节大小之前,我无法看到堆栈上的结构(在32位程序中)。当我超过64字节的限制时,结构被分配到堆栈上,但在
中仍然不可见!dso

class Program
{
    public struct StructTest
    {
        public long Id { get; set; }
        public long OtherId { get; set; }
        public long MoreSpace { get; set; }
        public long EvenMoreSpace { get; set; }
        public long MoreThan64Byte { get; set; }
    }

    private static void Main()
    {
        var c = new StructTest();
        c.Id = 51;
        c.OtherId = 52;
        c.MoreSpace = 53;
        c.EvenMoreSpace = 54;
        c.MoreThan64Byte = 55;
        Console.WriteLine("Attach the debugger now.");
        Console.ReadKey();
        Console.WriteLine(c.Id + c.OtherId + c.MoreSpace + c.EvenMoreSpace);
    }
}
这将被编译为

001a045e 0f57c0          xorps   xmm0,xmm0
001a0461 660fd607        movq    mmword ptr [edi],xmm0
001a0465 660fd64708      movq    mmword ptr [edi+8],xmm0
001a046a 660fd64710      movq    mmword ptr [edi+10h],xmm0
001a046f 660fd64718      movq    mmword ptr [edi+18h],xmm0
001a0474 660fd64720      movq    mmword ptr [edi+20h],xmm0

F:\Debugging\Source\StackOverflow\StructNotInHeap\Program.cs @ 19:
001a0479 c745d433000000  mov     dword ptr [ebp-2Ch],33h
001a0480 c745d800000000  mov     dword ptr [ebp-28h],0

F:\Debugging\Source\StackOverflow\StructNotInHeap\Program.cs @ 20:
001a0487 c745dc34000000  mov     dword ptr [ebp-24h],34h
001a048e c745e000000000  mov     dword ptr [ebp-20h],0

F:\Debugging\Source\StackOverflow\StructNotInHeap\Program.cs @ 21:
001a0495 c745e435000000  mov     dword ptr [ebp-1Ch],35h
001a049c c745e800000000  mov     dword ptr [ebp-18h],0

F:\Debugging\Source\StackOverflow\StructNotInHeap\Program.cs @ 22:
001a04a3 c745ec36000000  mov     dword ptr [ebp-14h],36h
001a04aa c745f000000000  mov     dword ptr [ebp-10h],0

F:\Debugging\Source\StackOverflow\StructNotInHeap\Program.cs @ 23:
001a04b1 c745f437000000  mov     dword ptr [ebp-0Ch],37h
001a04b8 c745f800000000  mov     dword ptr [ebp-8],0

这是首先初始化,然后将值移动到堆栈(EBP是当前堆栈指针)。

您的结构只有大约64位大,
s
只是一个局部变量,所以我猜它只存储在寄存器中。不需要把它放在堆栈或堆上。我怎样才能把它放在堆栈上?是否存在任何大小阈值?一般来说,在托管(安全)代码中,您根本不应该关心存储位置。内存管理由framework/cli完成,与您无关。@HenkHolterman我又添加了6个int字段,但结果仍然相同。有多大才算足够大?@RenéVogt:除非你遇到内存碎片问题、垃圾收集问题和其他问题。我认为了解.NET内存管理的工作原理是一个非常好的主意,包括理解OP要求的结构。@jimcrown:是的,在这种情况下,您的本地结构不存储在堆栈上,因为优化允许它存储在寄存器中。您问“为什么我在堆或线程堆栈对象中的任何位置都看不到StructTest的任何实例?”我解释了这一点。你到底还想知道什么?@jimcrown:好的,我明白了。也许我可以举个例子。让我试试…@ThomasWeller首先,你的语句没有指定local,但是也就是说,local可以在寄存器中,而不是堆栈中,如果它在
async
方法中,一个迭代器块中,它可以被编译器/抖动完全消除,或者被匿名方法关闭,它将被锁定到类的某个字段。@jimcrown:好的,找到一个。更新了答案您将看不到它,因为它不是对象。值类型(struct)的成员以内联方式存储。例如,对于具有两个整数成员的结构,在堆栈上或使用该结构处理两个整数的对象内分配存储。这是值类型和引用类型之间的区别。引用类型是堆上具有指针的对象。值类型的成员与使用它的类、函数等内联存储。