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

Why struct instance is not present in heap or on stack

我有以下代码。

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.WhosebugException
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\Whosebug\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\Whosebug\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\Whosebug\StructNotInHeap\Program.cs @ 19:
001a0479 c745d433000000  mov     dword ptr [ebp-2Ch],33h
001a0480 c745d800000000  mov     dword ptr [ebp-28h],0

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

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

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

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

即先初始化再入栈(EBP为当前栈指针)