新書推薦:
《
周秦之变的社会政治起源:从天子诸侯制国家到君主官僚制国家(历史政治学与中国政治学自主知识体系论丛)
》
售價:NT$
857.0
《
时刻人文·信用的承诺与风险:一个被遗忘的犹太金融传说与欧洲商业社会的形成
》
售價:NT$
469.0
《
同与不同:50个中国孤独症孩子的故事
》
售價:NT$
301.0
《
开宝九年
》
售價:NT$
250.0
《
摄影构图法则:让画面从无序到有序
》
售價:NT$
505.0
《
论僭政:色诺芬《希耶罗》义疏(含施特劳斯与科耶夫通信集)
》
售價:NT$
500.0
《
一个经济杀手的自白
》
售價:NT$
500.0
《
传播与流动:移民、手机与集装箱(新闻与传播学译丛·学术前沿系列)
》
售價:NT$
500.0
|
編輯推薦: |
为什么要阅读本书
正如本书第14章14.3节所提到的,如下类型的应用程序十分需要本书的知识。
·金融软件:特别是实时交易和所有需要基于大量各种数据以尽快得出答案的分析决策。
·大数据:虽然大数据通常都是批量、慢速处理,但如果处理每笔数据都慢一点点,累加起
来就会将总体处理时间拉长数小时或数天。此外,有些应用程序,比如搜索引擎,同样需要快速得到问题的答案。
·游戏:在一个以帧速率(FPS)决定游戏接受度和图像质量上限的世界中,每一毫秒都不能
浪费。
·机器学习:使用日益广泛的机器学习(ML)需要越来越强的计算能力去执行各种复杂算法。除了开发以上应用程序之外,正如本书第1章开头所说的,如果你想成为一名高级程序员,你不想止步于代码能够工作,还想有追求,关心工作的质量,关心它是如何工作的,那么你应该读这本书。
|
內容簡介: |
了解.NET内存管理的内部工作原理、陷阱和技术,以便有效地避免软件中出现各种性能和可伸缩性问题。尽管.NET具有自动内存管理功能,但了解 .NET的内存管理工作原理以及如何最好地编写与之高效交互的软件方面仍有许多益处。《.NET内存管理宝典 提高代码质量、性能和可扩展性》是你通过了解和处理 .NET 中的内存管理来编写出更好软件的全面指南。 《.NET内存管理宝典 提高代码质量、性能和可扩展性》经过了 Microsoft 的 .NET 团队全面审查,包含 了25 个有价值的故障排除方案,旨在帮助诊断具有挑战性的内存问题。读者还将受益于多个 .NET 内存管理”规则”,这些规则介绍了编写内存感知代码的方法以及避免常见的破坏性陷阱的方法。 本书内容 ● 了解自动内存管理的理论基础 ● 深入研究.NET内存管理的各个方面,包括对垃圾回收(GC)实现的详细介绍,这些知识如果自行摸索需要多年经验才能获得 ● 获得如何将这些知识应用于实际软件开发中的实用建议 ● 使用与 .NET 内存管理相关工具的实用知识来诊断各种与内存相关的问题 ● 探索高级内存管理的各个方面,包括使用Span和Memory类型
|
關於作者: |
Konrad Kokosa是一位经验丰富的软件设计师和开发人员。他对Microsoft公司的技术特别感兴趣,同时对其他所有技术也充满好奇。Konrad从事编程工作已经有十多年,解决过.NET世界中的许多性能问题和架构难题,设计和提升过.NET应用程序的运行速度。他是一名独立顾问,是meetup和技术会议讲师,喜欢写Twitter。Konrad还分享了他作为.NET领域培训讲师的激情岁月,特别是在应用程序性能、编码优秀实践和诊断方面。他是华沙Web性能小组的创始人。他是Visual Studio和开发工具类别中的Microsoft MVP。他是Dotnetos.org的联合创始人。Dotnetos.org由三位.NET爱好者发起,主要组织.NET性能相关的会议。
译 者 简 介
叶伟民
《.NET并发编程实战》的译者。曾在美国旧金山工作,具有16年的.NET开发经验,目前从业于金融科技行业。
涂曙光
前微软技术专家,专注于.NET和JavaScript技术领域。目前在私募基金行业从事低延迟交易系统的开发。
|
目錄:
|
第1章 基本概念 1
1.1 内存相关术语 2
1.1.1 静态分配 6
1.1.2 寄存器机 6
1.1.3 堆栈(Stack) 7
1.1.4 堆栈机 11
1.1.5 指针 12
1.1.6 堆(Heap) 14
1.2 手动内存管理 15
1.3 自动内存管理 19
1.4 引用计数 23
1.5 跟踪回收器(Tracking Collector ) 26
1.5.1 标记阶段 27
1.5.2 回收阶段 30
1.6 小历史 32
1.7 本章小结 34
规则1 –; 自学 34
第2章 底层内存管理 37
2.1 硬件 37
2.1.1 内存 42
2.1.2 CPU 43
2.2 操作系统 55
2.2.1 虚拟内存 55
2.2.2 large page 59
2.2.3 虚拟内存碎片 59
2.2.4 通用内存布局 59
2.2.5 Windows内存管理 60
2.2.6 Windows内存布局 65
2.2.7 Linux内存管理 67
2.2.8 Linux内存布局 68
2.2.9 操作系统的影响 69
2.3 NUMA和CPU组 70
2.4 本章小结 71
规则2 –; 避免随机访问,拥抱循序访问 71
规则3 –; 提高空间和时间数据局部性 72
规则4 –; 不要放弃使用更高级技巧的可能性 72
第3章 内存测量 73
3.1 尽早测量 74
3.1.1 开销和侵入性 74
3.1.2 采样与跟踪 75
3.1.3 调用树 75
3.1.4 对象图 76
3.1.5 统计 77
3.1.6 延迟与吞吐量 79
3.1.7 内存转储、跟踪、实时调试 80
3.2 Windows环境 81
3.2.1 概述 81
3.2.2 VMMap 81
3.2.3 性能计数器 82
3.2.4 Windows事件跟踪 87
3.2.5 Windows性能工具包 95
3.2.6 PerfView 104
3.2.7 ProcDump, DebugDiag 111
3.2.8 WinDbg 112
3.2.9 反汇编程序和反编译程序 114
3.2.10 BenchmarkDotNet 114
3.2.11 商业工具 115
3.3 Linux环境 123
3.3.1 概况 123
3.3.2 Perfcollect 124
3.3.3 Trace Compass 126
3.3.4 内存转储 134
3.4 本章小结 135
规则5 –; 尽早测量GC 137
第4章 .NET基础知识 139
4.1 .NET版本 139
4.2 .NET内部原理 141
4.3 程序集和应用程序域 148
4.4 进程内存区域 150
4.4.1 场景4-1:我的程序占用了多大内存 153
4.4.2 场景4-2:我的程序的内存使用率持续攀升(1) 155
4.4.3 场景4-3:我的程序的内存使用率持续攀升(2) 157
4.4.4 场景4-4:我的程序的内存使用率持续攀升(3) 158
4.5 类型系统 161
4.5.1 类型的分类 161
4.5.2 类型的存储 162
4.5.3 值类型 163
4.5.4 引用类型 169
4.6 字符串 173
4.6.1 字符串暂存 178
4.6.2 场景4-5:我的程序的内存使用率太高 182
4.7 装箱与拆箱 185
4.8 按引用传递 188
4.8.1 按引用传递值类型实例 188
4.8.2 按引用传递引用类型实例 189
4.9 类型数据局部性 190
4.10 静态数据 193
4.10.1 静态字段 193
4.10.2 静态数据揭秘 194
4.11 本章小结 197
规则6 –; 测量你的程序 197
规则7 –; 不要假设内存泄漏不存在 198
规则8 –; 考虑使用结构 198
规则9 –; 考虑使用字符串暂存 198
规则10 –; 避免装箱 198
第5章 内存分区 201
5.1 分区策略 201
5.2 按大小分区 202
5.2.1 小对象堆 203
5.2.2 大对象堆 203
5.3 按生存期分区 207
5.3.1 场景5-1:我的程序健康吗?
实时的代大小 210
5.3.2 记忆集 214
5.3.3 卡表(Card Tables) 218
5.3.4 卡包(Card Bundles) 222
5.4 按物理分区 224
5.4.1 场景5-2:nopCommerce可能发生内存泄漏 229
5.4.2 场景5-3:大对象堆浪费了 236
5.4.3 段和堆解析 237
5.4.4 段重用 239
5.5 本章小结 241
规则11 –; 监视代大小 241
规则12 –; 避免不必要的堆引用 241
规则13 –; 监视段使用情况 242
第6章 内存分配 243
6.1 内存分配简介 243
6.2 bump pointer分配 244
6.3 空闲列表分配 250
6.4 创建新对象 253
6.4.1 小对象堆分配 255
6.4.2 大对象堆分配 257
6.5 堆再平衡 260
6.6 OutOfMemoryException异常 262
场景6-1:OutOfMemoryException异常 263
6.7 堆栈分配 265
6.8 避免分配 266
6.8.1 显式的引用类型分配 267
6.8.2 隐式的分配 286
6.8.3 类库中的各种隐式分配 293
6.8.4 场景6-2:调查程序中的分配情况 297
6.8.5 场景6-3:Azure Functions 299
6.9 本章小结 300
规则14 –; 在性能攸关的地方,
避免堆分配 300
规则15 –; 避免过多的LOH分配 301
规则16 –; 如果可行,在堆栈上分配 301
第7章 垃圾回收——简介 303
7.1 高层视图 303
7.2 GC过程的示例 304
7.3 GC过程的步骤 309
场景7-1:分析GC的使用情况 309
7.4 分析GC 313
7.5 垃圾回收性能调优数据 314
7.5.1 静态数据 314
7.5.2 动态数据 315
7.5.3 场景7-2:了解分配预算 317
7.6 回收触发器 325
7.6.1 分配触发器 326
7.6.2 显式触发器 326
7.6.3 场景7-3:分析显式GC调用 328
7.6.4 低内存级别系统触发器 333
7.6.5 各种内部触发器 333
7.7 EE挂起 334
场景7-4:分析GC挂起时间 335
7.8 要判决的代 336
场景7-5:被判决的代的分析 338
7.9 本章小结 339
第8章 垃圾回收——标记阶段 341
8.1 对象的遍历与标记 341
8.2 局部变量根 342
8.2.1 局部变量存储 343
8.2.2 堆栈根 343
8.2.3 词法作用域 343
8.2.4 存活堆栈根与词法作用域 344
8.2.5 带有激进式根回收的存活堆栈根 345
8.2.6 GC信息 350
8.2.7 固定局部变量 354
8.2.8 堆栈根扫描 356
8.3 终结根 357
8.4 GC内部根 357
8.5 GC句柄根 358
8.6 处理内存泄漏 363
8.6.1 场景8-1:nopCommerce可能
发生内存泄漏 365
8.6.2 场景8-2:找出常出现的根 367
8.7 本章小结 369
第9章 垃圾回收——计划阶段 371
9.1 小对象堆 371
9.1.1 插头和间隙 371
9.1.2 场景9-1:具有无效结构的
内存转储 375
9.1.3 砖表 376
9.1.4 固定 377
9.1.5 场景9-2:调查固定 381
9.1.6 代边界 385
9.1.7 降级 385
9.2 大对象堆 389
9.3 压缩的决策 390
9.4 本章小结 391
第10章 垃圾回收——清除和压缩 393
10.1 清除阶段 393
10.1.1 小对象堆 393
10.1.2 大对象堆 394
10.2 压缩阶段 394
10.2.1 小对象堆 394
10.2.2 大对象堆 397
10.2.3 场景10-1:大对象堆的碎片化 398
10.3 本章小结 404
规则17 –; 观察运行时挂起 405
规则18 –; 避免“中年危机” 406
规则19 –; 避免老的代和LOH碎片化 406
规则20 –; 避免显式GC 407
规则21 –; 避免内存泄漏 407
规则22 –; 避免固定 407
第11章 GC风格 409
11.1 模式概述 409
11.1.1 工作站模式与服务器模式 409
11.1.2 非并发模式与并发模式 411
11.2 模式配置 411
11.2.1 .NET Framework 412
11.2.2 .NET Core 412
11.3 GC停顿和开销 413
11.4 模式描述 414
11.4.1 非并发工作站模式 415
11.4.2 并发工作站模式(4.0版本之前) 416
11.4.3 后台工作站模式 417
11.4.4 非并发服务器模式 423
11.4.5 后台服务器模式 425
11.5 延迟模式 426
11.5.1 批处理模式 426
11.5.2 交互式模式 426
11.5.3 低延迟模式 427
11.5.4 持续低延迟模式 427
11.5.5 无GC区域模式 429
11.5.6 延迟优化目标 430
11.6 选择GC风格 431
11.6.1 场景11-1:检查GC设置 432
11.6.2 场景11-2:对不同GC模式进行基准测试 433
11.7 本章小结 438
规则23 –; 有意识地选择GC模式 439
规则24 –; 记住延迟模式的相关知识 439
第12章 对象生存期 441
12.1 对象与资源的生命周期 441
12.2 终结 442
12.2.1 简介 443
12.2.2 激进式根回收问题 446
12.2.3 关键终结器 449
12.2.4 终结的内部实现 449
12.2.5 场景12-1:由于终结而导致的内存泄漏 455
12.2.6 复活(Resurrection) 460
12.3 Disposable对象 463
12.4 安全句柄 468
12.5 弱引用 473
12.5.1 缓存 477
12.5.2 弱事件模式 479
12.5.3 场景12-2:由于事件导致的内存泄漏 484
12.6 本章小结 486
规则25 –; 避免终结器 486
规则26 –; 显式清理 487
第13章 其他主题 489
13.1 依赖句柄 489
13.2 线程局部存储 494
13.2.1 线程静态字段 494
13.2.2 线程数据插槽 497
13.2.3 线程局部存储的内部 498
13.2.4 使用场景 503
13.3 托管指针 504
13.3.1 ref局部变量 505
13.3.2 ref返回值 505
13.3.3 只读ref变量和in参数 507
13.3.4 ref类型的内部 511
13.3.5 C#中的托管指针——ref变量 521
13.4 关于结构的更多知识 526
13.4.1 只读结构 526
13.4.2 ref结构(类似于托管指针的类型) 528
13.4.3 固定大小的缓冲区 529
13.5 对象/结构布局 533
13.6 非托管约束 541
13.7 本章小结 546
第14章 高级技巧 547
14.1 Span和Memory 547
14.1.1 Span 547
14.1.2 Memory 560
14.1.3 IMemoryOwner 562
14.1.4 Memory的内部实现 566
14.1.5 Span和Memory使用准则 568
14.2 Unsafe 568
14.3 面向数据设计 573
14.3.1 战术型设计 574
14.3.2 战略型设计 576
14.4 未来特性 585
14.4.1 可为空引用类型 585
14.4.2 Pipelines 590
14.5 本章小结 595
第15章 编程API 597
15.1 GC API 597
15.1.1 收集数据和统计 597
15.1.2 GC通知 604
15.1.3 控制非托管内存压力 606
15.1.4 显式回收 606
15.1.5 无GC区域 606
15.1.6 终结(Finalization)管理 606
15.1.7 内存使用率 607
15.1.8 GC类中的内部调用 608
15.2 CLR Hosting 609
15.3 ClrMD 616
15.4 TraceEvent库 621
15.5 自定义GC 623
15.6 本章小结 626
|
內容試閱:
|
在计算机科学的历史中,内存一直存在——从穿孔卡片到磁带,再到如今复杂的DRAM芯片。它也将会永远存在,可能会以科幻全息芯片的形式出现,或者甚至是我们现在无法想象的更神奇的事物。当然,内存的存在并非没有原因。众所周知,计算机程序被认为是结合在一起的算法和数据结构。我非常喜欢这句话。大概多数人都听说过Niklaus Wirth撰写的Algorithms Data Structures=Programs一书,正是在这本书中创造了这个伟大的句子。
在软件工程领域的早时期,内存管理就以其重要性而闻名。从台计算机开始,工程师就必须考虑算法(程序代码)和数据结构(程序数据)的存储。如何加载和存储以及在何处加载和存储这些数据以供以后使用一直都很重要。
在这方面,软件工程和内存管理总是天生相关的,就像软件工程和算法一样。我相信它将永远都是这样。内存是一种有限的资源,而且将永远都是。因此,在某些时候或某种程度上,内存将永远留在未来的开发人员的脑海中。如果一种资源是有限的,那么总会有某种错误或滥用导致该资源的匮乏。内存也不例外。
话虽如此,关于内存管理,有一件事是肯定不断变化的——就是内存的大小。早期的开发人员都清楚地知道他们程序里内存的每一位(bit),那时他们只有几千字节(KB)的内存。每十年这个数字都在增长,今天,内存的单位为吉字节(GB),而太字节(TB)和拍字节(PB)则在敲门。随着内存大小的增加,访问数据的时间将会减少,从而有可能在令人满意的时间内处理完所有这些数据。但是,即使我们可以说内存处理是很快的,使用简单的内存管理算法来尝试处理所有GB量级的数据而不进行任何优化和更复杂的调整也是不可行的。这主要是因为内存访问时间的增长速度比使用它们的CPU的处理能力要慢。因此必须格外小心,以免造成内存访问瓶颈,从而限制CPU的能力。
这使得内存管理不仅至关重要,而且是计算机科学中非常有趣的一部分。自动的内存管理虽然能够使其变得更好,但这并不像“释放未使用的对象”这么简单的一句话那么容易。所要管理的内容、如何以及何时进行内存管理这些简单方面都使其成为持续不断的改进旧算法和发明新算法的过程。无数的科学论文和博士学位论文正在考虑如何以方式来自动地管理内存。诸如国际内存管理专题研讨会(ISMM)的活动每年都会展示在该领域做了多少工作,比如在垃圾回收、动态分配以及与运行时、编译器和操作系统的交互等方面。然后,这些学术研讨会转变为我们在日常工作中使用的商业化和开源产品。
.NET是托管环境的一个完美示例,在该环境中,所有这些复杂性都被隐藏在底层,开发人员可以将其作为一个令人愉快的、随时可用的平台使用。确实,我们可以在不用了解这些底层复杂性的情况下使用它,这是.NET的一项伟大成就。然而,我们的程序对性能敏感度方面的需求越强,就越不可能避免要获得关于底层工作方式和原因的任何知识。而且,在我看来,了解我们每天使用的东西是多么有趣啊!
我写《.NET内存管理宝典》的方式是很多年前当我开始进入.NET性能和诊断领域时我喜欢阅读文献资料的一种方式。因此,《.NET内存管理宝典》并非从传统的有关每代堆(Heap)和堆栈(Stack)的介绍或说明开始。相反,我会从内存管理的基础知识和原理开始。换言之,我会试着用一种能让你感觉到这是一个非常有趣的话题的方式来写《.NET内存管理宝典》,而不仅是展示了“这里有一个.NET垃圾回收器,它能做这做那”。我不但提供了这些知识的内容,而且提供这些知识的工作方式,更重要的是讲解.NET内存管理的幕后原理。因此,你以后读到的关于这个话题的所有内容都应该能够更容易地理解。我会试图用不仅仅是与.NET相关的通用性知识来启发你,尤其是在前两章。这将带来对该主题的更深入理解,这通常也适用于其他软件工程任务(即基于对算法、数据结构和其他优秀工程技术的理解)。
我想以一种让每个.NET开发人员都满意的方式来编写《.NET内存管理宝典》。无论你有没有编程经验,都应该能在《.NET内存管理宝典》中找到一些有趣的知识。当我们从基础开始时,初级程序员很快就会有机会深入了解.NET内部。更高级的程序员会发现许多更有趣的实现细节。重要的是,无论经验多少,每个人都能够从所提供的实用示例代码和问题诊断中受益。
因此,从《.NET内存管理宝典》中获得的知识应该能够帮助你编写更好的代码 ——更好地了解性能和内存,并充分利用相关功能,而不必先读完所有内容。这些知识还可以提高应用程序的性能和可扩展性——代码要面向的内存越多,则对资源瓶颈的暴露和资源利用率就越少。
我希望所有这些能使这本书比简单描述.NET框架及其内部状态更全面和持久。不管未来.NET框架会如何发展,我相信《.NET内存管理宝典》中的大部分知识在很长一段时间内都是正确的。即使某些实施细节会发生变化,由于《.NET内存管理宝典》的知识,你也应该能够很容易地理解它们,因为基本原则不会变化得这么快。祝你在自动内存管理这个庞大而有趣的主题中度过一段愉快的时光!
说到这里,我还想强调《.NET内存管理宝典》中没有特别提到的内容。内存管理的主题,尽管乍一看似乎非常专业且狭窄,但其实却出人意料地广泛。虽然我涉及了很多主题,但由于篇幅所限,有时无法按我希望的方式详细介绍它们。这些省略掉的主题包括对其他托管环境(如 Java、Python或Ruby)的全面参考。我也要向F#粉丝道歉,因为很少提到这种语言。因为没有足够的页面来简单地进行过硬的描述,我不想发布任何不全面的内容。我本来想对Linux环境给予更多的关注,但是这个工具主题太新了,在编写《.NET内存管理宝典》时,我只在第3章给你一些建议(出于同样的原因也完全省略了macOS世界)。显然,类似的,我还省略了大部分其他与内存无关的性能部分,如多线程主题。
其次,尽管我已经尽了努力来介绍所讨论的主题和技术的实际应用,但是不可能做到覆盖所有方面和尽善尽美。实际应用实在是太多了。我更希望读者能全面阅读,并结合日常工作来重新思考主题并运用起来,从而掌握该知识。当了解到某事物的工作原理后,你将能够使用它!这尤其包括书中场景。请注意,《.NET内存管理宝典》中包含的所有场景都是出于演示目的。它们的代码被精简到限度,以便更容易地展示出单个问题的根本原因。被观察到的不当行为背后可能有多种原因(例如,许多方法可能会发现有托管内存泄漏)。使用场景这种编写方式有助于用单个示例原因来说明此类问题,因为很显然不可能在一本书中包含所有可能的原因。此外,在真实场景中,你的调查将充斥着大量嘈杂的数据和虚假的调查路径。通常没有单一的方法能解决所描述的问题,但是有许多方法可以在问题分析期间找到根本原因。这使得这种故障排除是纯粹的工程任务与一点点由你的直觉支撑的艺术的混合体。另外请注意,场景有时是会相互引用的,以免重复相同的步骤、图形和描述。
在这本书中,我专门避免提及各种特定技术的案例和问题的来源。这会包含太多的技术细节。如果我是在10年前写这本书,我可能不得不列出ASP.NET WebForms 和 WinForms 中内存泄漏的各种典型场景。如果是在几年前写,则要列出ASP.NET MVC、WPF、WCF、WF……如果现在写,就要列出ASP.NET Core、EF Core、Azure Functions等。说到这里,我希望你能够明白我所说的意思。这样的知识很快就会过时。这本书如果充满了WCF内存泄漏的例子,那么今天将几乎没有人会感兴趣。我的一个超级粉丝说:“授人以鱼,你养活他一天;授人以渔,你将养活他一辈子”。因此,《.NET内存管理宝典》中的所有知识、所有场景,都在教你如何捕鱼。如果你拥有了足够的知识和理解,那么所有问题,无论潜在的特定技术如何,都可以同样的方式得到诊断和解决。
所有这一切也使得阅读这本书相当困难,因为有时书中充满了细节,也许还有海量的信息。无论如何,我鼓励你深入而缓慢地阅读,抵制只是略微一读的诱惑。例如,要想充分利用《.NET内存管理宝典》,应该仔细研究展示和呈现的代码(而不仅仅是看它们一眼而已,这些代码是很常见的,因此它们很容易被忽略)。
我们生活在一个好时代,因为CoreCLR 运行时开源了。这使得对CLR 运行时的理解能力更上一层楼。不需要猜测,也没有奥秘,一切都在代码中,都可以读取和理解。因此,我的研究主要是基于CoreCLR的GC代码(该部分代码和 .NET Framework的GC代码是相同的)。我花了无数个日夜来分析这些大量的出色的工程工作。我认为它们棒极了,我相信有些人也会喜欢研究著名的gc.cpp文件,它有好几万行代码。然而,它的学习曲线非常陡峭。为了帮助你实现这一点,我经常会留下一些线索,对所描述的主题会说明可以在CoreCLR的何处代码中进行研究。我建议请随时从gc.cpp文件获得更深刻的理解。
阅读完《.NET内存管理宝典》后,你应该能够:
● 在.NET中编写有性能和内存意识的代码。尽管所提供的示例是用C#编写的,但是我相信你在此获得的理解和工具箱,也同样可以应用于F#或VB.NET。
● 诊断与.NET内存管理有关的典型问题。由于大多数技术都基于ETW/LLTng数据和SOS扩展,因此它们在Windows和Linux上都适用(Windows上的工具会更高级)。
● 理解CLR在内存管理领域的工作原理。我花了很多精力来解释它的运作方式和原因。
● 充分理解GitHub上许多有趣的C#和CLR运行时问题,甚至能以你自己的想法参与其中。
● 阅读CoreCLR(特别是gc.cpp)文件中的GC代码,并充分理解,以便进行进一步的调查和研究。
● 充分了解有关Java、Python或Go等不同环境中GC和内存管理的信息。
《.NET内存管理宝典》的内容大概如下。
第1章是对内存管理的非常笼统的理论介绍,几乎没有提到.NET。第2章同样是硬件和操作系统级别的内存管理的通用性介绍。这两章都可以作为一个重要的、但可选的介绍。它们对该主题进行了有益的、更广泛的研究,对《.NET内存管理宝典》的其余部分很有用。虽然我强烈建议你阅读它们,但如果你急于学习实用的与.NET相关的主题,则可以忽略它们。给高级读者的一个提示——即使你认为前两章的主题对你来说过于浅显,也请阅读它们。我已尝试把那些你可能感兴趣的信息包括进去。
第 3 章专门介绍测量和各种工具(其中的某些工具在《.NET内存管理宝典》后面会经常用到)。这一章主要包含了一个工具列表以及如何使用这些工具。如果你主要对《.NET内存管理宝典》的理论部分感兴趣,你可以只略读这一章。另一方面,如果你打算在诊断问题时大量使用《.NET内存管理宝典》的知识,你可能会经常回顾这一章。
第4章是我们开始深入讨论.NET的第1章,同时仍然依旧以一种通用的方式让我们理解一些相关的内部结构,如.NET类型系统(包括值类型与引用类型)、字符串内联或静态数据。如果你真的很着急,不妨从这一章开始阅读。第5章介绍了个真正的内存相关主题——.NET应用程序中的内存组织方式,介绍了小对象堆(Small Object Heap,SOH)和大对象堆(Large Object Heap,LOH)的概念以及段(Segment)的概念。第6章进一步探讨与内存相关的内部结构,这些内部结构专门用于分配内存。出乎一些人意料的是,用了相当大篇幅的一章来专门针对这一理论上简单的主题。该章占用了相当的篇幅来讲述一个重要的部分:对各种分配来源的描述,从而避免这些情况的出现。
第7~10章是描述GC如何在.NET中工作的核心部分,并附有结合这些知识的实际示例和注意事项。为了不让读者因同时接收了太多信息而不知所措,这些章节介绍了GC简单的方式——非并发工作站。另一方面,第11章专门介绍了所有其他方式,并综合考虑了可供选择的各种方式。第12章总结了《.NET内存管理宝典》的GC部分,描述了三种重要的机制:终结(Finalization)、Disposable对象和弱引用。
后三章构成了《.NET内存管理宝典》的“高级”部分,解释.NET内存管理核心部分之外的工作原理。例如,第13章介绍了托管指针的主题,并更深入探讨结构(包括近添加的ref结构)。第14章对近越来越流行的类型和技术给予了越来越多的关注,比如Span和Memory类型。还有一节专门讨论面向数据设计这一鲜为人知的主题,以及关于近的C#特性(比如可以为空的引用类型和管道)等做了简单介绍。第15章,也就是后一章,描述了如何从代码中控制和监视GC,包括GC类API、CLR Hosting或ClrMD库。
《.NET内存管理宝典》的大部分源代码清单可在配套的GitHub代码库https://github.com/Apress/pro-.net-memory找到,也可扫描封底二维码下载。它们按章节组织,并且其中大多数包含了两个解决方案:一个用于执行基准,另一个用于其他代码清单。请注意,虽然所包含的项目包含了《.NET内存管理宝典》的代码清单,但通常会有更多的代码供你查看。如果你想使用或试验一个特定的代码清单,简单的方法就是搜索它的编号,并对它和它的用法进行试用。但我也鼓励你在项目中寻找特定的主题,以便更好地理解。
在此我想提及一下一些重要的约定。相关的一个是区分以下两个贯穿了《.NET内存管理宝典》的主要概念:
● 垃圾回收——回收不再需要的内存的过程(即大家通常所理解的)。
● 垃圾回收器——实现垃圾回收的特定机制,明显的是在.NET GC的上下文中。
《.NET内存管理宝典》自成一体,没有涉及许多其他材料或书籍。显然,我需要参考各种资源来获取很多伟大的知识来完成《.NET内存管理宝典》。下面列出了我选择的、作为补充知识来源的建议书籍和文章。
● Pro .NET Performance,Sasha Goldshtein, Dima Zurbalev, Ido Flatow著(Apress, 2012)
● CLR via C#, Jeffrey Richter著(Microsoft Press, 2012)
● Writing High-Performance .NET Code, Ben Watson著(Ben Watson, 2014)
● Advanced .NET Debugging, Mario Hewardt著(Addison-Wesley Professional, 2009)
● .NET IL Assembler, Serge Lidin著(Microsoft Press, 2012)
● Shared Source CLI Essentials, David Stutz著(O’Reilly Media, 2003)
● “Book of The Runtime”运行时开发团队自己编写的开源文档,可通过https://github.com/ dotnet/coreclr/blob/master/Documentation/botr/README.md访问。
此外,还有大量的知识散落在各种在线博客和文章中。但是,与其用这些页面列表来充斥《.NET内存管理宝典》版面,不如将你重定向到由Adam Sitnik维护的一个伟大的代码库https://github.com/adamsitnik/awesome- dot-net-performance。
|
|