一、简介
     今天是《Net 高级调试》的第六篇文章。记得我刚接触 Net 框架的时候,还是挺有信心的,对所谓的值类型和引用类型也能说出自己的见解,毕竟,自己一直在努力。当然这些见解都是书本上的,并没有做到眼见为实,所以总是有些东西说不清楚。今天,我们就好好的说说 C# 的类型,是从内存级别、从底层来说一下值类型、引用类型到底是什么,它们在内存中的形态,还有也说说数组的内存形态,如何内部布局的,以及我们如何查找由未捕捉的异常引起的程序崩溃。这些都是基础的,如果这些掌握不好,以后的高级调试的道路,也不好走。自从我过了这一关,很多东西理解起来,比较透彻 了,但是,还必须努力。当然了,第一次看视频或者看书,是很迷糊的,不知道如何操作,还是那句老话,一遍不行,那就再来一遍,还不行,那就再来一遍,俗话说的好,书读千遍,其意自现。
     如果在没有说明的情况下,所有代码的测试环境都是 Net Framewok 4.8,但是,有时候为了查看源码,可能需要使用 Net Core 的项目,我会在项目章节里进行说明。好了,废话不多说,开始我们今天的调试工作。

       调试环境我需要进行说明,以防大家不清楚,具体情况我已经罗列出来。
          操作系统:Windows Professional 10
          调试工具:Windbg Preview(可以去Microsoft Store 去下载)
          开发工具:Visual Studio 2022
          Net 版本:Net Framework 4.8
          CoreCLR源码:源码下载

二、基础知识
    1、对象检查
        1.1、简介
            高级调试的目标就是分析应用程序故障所形成的原因,既然是故障,大多数情况下是对象的某种损坏,这就需要我们深入的了解各种对象的审查方法。

    2、各种检查方法
        2.1、内存转储
              这个方式非常底层,从内存地址上观察地址上的内容,常使用【dp】命令。出来【dp】命令,还有其他一些命令,比如:du,dw,db,da 等,如果想了解更多,可以查看 Windbg 的帮助文档,命令是【.hh】。

        2.2、对值类型的转储
              对值类型的转储非常的简单,一般通过【dp】命令观察内存地址,从内存上提取内容,可以在结束处观察 esp 指针,当然,如果知道栈地址,我们也可以使用【dp】命令查看内容,效果和查看 esp 是一样的。

        2.3、对应用类型的转储
              如果我们想查看引用类型的内容,我们可以使用【!do】命令,查看应用类型的内容。

        2.4、对数组的转储
              C# 数组的内存布局,大概有两种形式,值类型和引用类型。
              1)值类型数组。
                  结构:同步块索引,方法表,数组大小,数组元素1,数组元素2......

              2)引用类型数组。
                  结构:同步块索引,方法表,数组大小,数组元素1,数组元素2......
                                      |      |
                               |------------------+      +--------------------|
                               |                       |
                            同步块索引、方法表、内容        同步块索引、方法表、内容

        2.5、对异常的转储
              有时候,程序存在未处理的异常导致的崩溃,在事后分析 dump 中要是能找到这个异常,对我们分析和解决问题会有很大的帮助。如果我们想查看异常的详情,我们可以使用【!pe(print exception)】命令,打印异常的详情,当然也可以使用【!do】命令查看更详细的异常内容。

三、调试过程
    废话不多说,这一节是具体的调试操作的过程,又可以说是眼见为实的过程,在开始之前,我还是要啰嗦两句,这一节分为两个部分,第一部分是测试的源码部分,没有代码,当然就谈不上测试了,调试必须有载体。第二部分就是根据具体的代码来证实我们学到的知识,是具体的眼见为实。
    1、测试源码
        1.1、Example_6_1_1
Net 高级调试之六:对象检查之值类型、引用类型、数组和异常的转储-LMLPHPNet 高级调试之六:对象检查之值类型、引用类型、数组和异常的转储-LMLPHP
 1 namespace Example_6_1_1
 2 {
 3     internal class Program
 4     {
 5         static void Main(string[] args)
 6         {
 7             var person = new Person();
 8 
 9             Console.ReadLine();
10         }
11     }
12 
13     internal class Person
14     {
15         public int Age = 20;
16 
17         public string Name = "jack";
18     }
19 }
11-08 19:13