草庐IT

c# - 有什么工具可以在.NET 程序执行时查看变量的存储位置?是在栈上还是堆上?

coder 2023-07-11 原文

很久以前,我想知道变量(无论是值类型还是引用类型)的确切存储位置。它会在堆栈上还是堆上?

我已阅读 Eric Lippert’s article一样的。

出于好奇,我想要的是交叉验证我所理解的相同内容。是否存在相同的工具?或者我会以任何方式知道,当 .NET 程序正在执行时,哪些变量会存储在堆栈中?哪个存储在堆上?

谢谢

最佳答案

将存储按堆栈和堆划分的想法是一种方便的抽象,可以很好地为您服务。但更复杂的是,.NET 程序中有 6 个不同的变量存储位置。

这里选择的工具是调试器,它可以准确显示变量的存储位置。这确实需要深入了解机器代码的工作原理。使用 Debug + Windows + Disassembly 查看机器码。查看程序的发布版本并更改设置,即使在调试时也能优化代码,这一点也很重要。工具 + 选项、调试、常规,取消勾选“模块加载时抑制 JIT 优化”选项。您现在将看到机器代码将在您的用户机器上执行的方式。

你必须事先知道的事情才能理解这一切:

  • 引用类型的对象存储在 GC 堆上。存储引用的变量与值类型值具有相同类型的存储选择。
  • 值类型值或对象引用有六个可能的存储位置:
  • 如果变量是引用类型
  • 的成员,则它们存储在 GC 堆上。
  • 如果变量声明为静态
  • ,它们将存储在 AppDomain 的加载程序堆中。
  • 如果变量为 [ThreadStatic]
  • ,则它们存储在线程本地存储中
  • 如果变量是方法参数或局部变量
  • ,它们可以存储在堆栈帧中。
  • 如果变量是方法参数或局部变量,它们可以存储在 CPU 寄存器中
  • 特定于 x86 抖动,Single 或 Double 类型的变量可以存储在 FPU 堆栈中。

  • 后三个要点是它变得复杂的地方,以及为什么您需要查看机器代码以找出它们的存储位置。它是高度特定于实现的,抖动的类型很重要。并且受您是否启用抖动优化器的影响很大。在这里做出正确的选择对于性能非常重要。粗略的轮廓(跳过ARM抖动):
  • 前两个方法参数存储在 x86 抖动的 CPU 寄存器中,包括实例方法的 this 值。 x64 抖动使用 4 个寄存器。浮点处理器寄存器用于传递 x86 上的 Single 和 Double 类型的变量,x64 上的 XMM 寄存器
  • 函数返回值在 CPU 寄存器中返回,如果合适,使用 EAX 或 RAX 寄存器,如果它是浮点值,则使用 ST0。如果它不适合,则调用者在堆栈帧上为该值保留空间并将指针传递给它
  • 抖动优化器寻找机会在 CPU 寄存器中存储局部变量。如果因为寄存器用完而被迫这样做,它可能会将寄存器溢出回堆栈帧。

  • 这些实现细节有许多可观察到的副作用:
  • 获取存储在 cpu 寄存器中的局部变量会使代码难以调试。调试器对存储位置的了解不够。这是 Debug 版本存在的主要原因,它抑制了优化,因此您可以轻松检查局部变量,调试器确实知道用于变量
  • 的堆栈帧槽。
  • 您无法检查方法的返回值,这在调试时会带来很大的不便。调试器对抖动选择的存储位置的了解不足以可靠地找到该值。编辑:在 VS2013 中修复
  • 由于变量被优化为存储在 cpu 寄存器中,因此您可能难以调试线程问题。在循环或 if() 语句中测试值会产生寄存器中值的副本,而不是存储在内存中的值。特别是 x86 抖动问题和 volatile 关键字的原因,该关键字抑制了此优化
  • 您可以初始化指向局部变量的指针,而无需固定它。与存储在 GC 堆中的变量可能会被垃圾回收移动并因此需要固定不同,局部变量具有固定的存储地址,该地址在整个方法体
  • 中均有效。
  • 为堆栈帧分配的空间量由抖动决定。但是可以自己分配一个块,C# stackalloc 关键字支持它。这是您可以直接分配的最快内存
  • 将浮点值存储在 FPU 寄存器中会导致浮点精度问题。当它存储在 FPU 中时,值以 80 位精度存储。但是当它溢出到内存时,它会被截断为 32 或 64 位精度。这种溢出何时发生的不可预测性(加上 x64 抖动的不同策略)会产生不同的浮点结果,如果计算丢失了很多有效数字,则微小的变化会导致计算结果的巨大差异。
  • 关于c# - 有什么工具可以在.NET 程序执行时查看变量的存储位置?是在栈上还是堆上?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14022680/

    有关c# - 有什么工具可以在.NET 程序执行时查看变量的存储位置?是在栈上还是堆上?的更多相关文章

    1. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

      类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

    2. ruby-on-rails - Ruby net/ldap 模块中的内存泄漏 - 2

      作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代

    3. ruby - 在 Ruby 程序执行时阻止 Windows 7 PC 进入休眠状态 - 2

      我需要在客户计算机上运行Ruby应用程序。通常需要几天才能完成(复制大备份文件)。问题是如果启用sleep,它会中断应用程序。否则,计算机将持续运行数周,直到我下次访问为止。有什么方法可以防止执行期间休眠并让Windows在执行后休眠吗?欢迎任何疯狂的想法;-) 最佳答案 Here建议使用SetThreadExecutionStateWinAPI函数,使应用程序能够通知系统它正在使用中,从而防止系统在应用程序运行时进入休眠状态或关闭显示。像这样的东西:require'Win32API'ES_AWAYMODE_REQUIRED=0x0

    4. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

      我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

    5. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

      我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

    6. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

      我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

    7. ruby - 使用 Vim Rails,您可以创建一个新的迁移文件并一次性打开它吗? - 2

      使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta

    8. ruby - 为什么 4.1%2 使用 Ruby 返回 0.0999999999999996?但是 4.2%2==0.2 - 2

      为什么4.1%2返回0.0999999999999996?但是4.2%2==0.2。 最佳答案 参见此处:WhatEveryProgrammerShouldKnowAboutFloating-PointArithmetic实数是无限的。计算机使用的位数有限(今天是32位、64位)。因此计算机进行的浮点运算不能代表所有的实数。0.1是这些数字之一。请注意,这不是与Ruby相关的问题,而是与所有编程语言相关的问题,因为它来自计算机表示实数的方式。 关于ruby-为什么4.1%2使用Ruby返

    9. ruby - 我可以使用 Ruby 从 CSV 中删除列吗? - 2

      查看Ruby的CSV库的文档,我非常确定这是可能且简单的。我只需要使用Ruby删除CSV文件的前三列,但我没有成功运行它。 最佳答案 csv_table=CSV.read(file_path_in,:headers=>true)csv_table.delete("header_name")csv_table.to_csv#=>ThenewCSVinstringformat检查CSV::Table文档:http://ruby-doc.org/stdlib-1.9.2/libdoc/csv/rdoc/CSV/Table.html

    10. ruby-on-rails - 如何使用 instance_variable_set 正确设置实例变量? - 2

      我正在查看instance_variable_set的文档并看到给出的示例代码是这样做的:obj.instance_variable_set(:@instnc_var,"valuefortheinstancevariable")然后允许您在类的任何实例方法中以@instnc_var的形式访问该变量。我想知道为什么在@instnc_var之前需要一个冒号:。冒号有什么作用? 最佳答案 我的第一直觉是告诉你不要使用instance_variable_set除非你真的知道你用它做什么。它本质上是一种元编程工具或绕过实例变量可见性的黑客攻击

    随机推荐