为什么 FREE 块在堆中这么大
Why FREE block so large in heap
我有一个使用 C# 和 WPF 构建的应用程序,运行 Windows 8。C# 代码使用 Interop 调用外部 C++ dll。经过一系列的操作,内存占用接近2GB,怀疑是内存泄漏。我创建了进程转储并尝试使用 WinDbg 分析它,通过命令 !address -summary
,我收到以下消息:
--- Usage Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
Free 482 7ff`4d11b000 ( 7.997 Tb) 99.97%
<unknown> 1208 0`7573d000 ( 1.835 Gb) 65.64% 0.02%
Heap 1664 0`1e1ab000 ( 481.668 Mb) 16.82% 0.01%
Image 1294 0`19f98000 ( 415.594 Mb) 14.52% 0.00%
Stack 261 0`053ce000 ( 83.805 Mb) 2.93% 0.00%
Other 23 0`001d8000 ( 1.844 Mb) 0.06% 0.00%
TEB 87 0`000ae000 ( 696.000 kb) 0.02% 0.00%
PEB 1 0`00001000 ( 4.000 kb) 0.00% 0.00%
--- Type Summary (for busy) ------ RgnCount ----------- Total Size -------- %ofBusy %ofTotal
MEM_PRIVATE 2634 0`922ae000 ( 2.284 Gb) 81.69% 0.03%
MEM_IMAGE 1787 0`1b38d000 ( 435.551 Mb) 15.21% 0.01%
MEM_MAPPED 90 0`05807000 ( 88.027 Mb) 3.07% 0.00%
--- State Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
MEM_FREE 509 7ff`4d1ae000 ( 7.997 Tb) 99.97%
MEM_COMMIT 3346 0`9205e000 ( 2.282 Gb) 81.61% 0.03%
MEM_RESERVE 1165 0`20de4000 ( 525.891 Mb) 18.37% 0.01%
--- Protect Summary (for commit) - RgnCount ----------- Total Size -------- %ofBusy %ofTotal
PAGE_READWRITE 1577 0`73295000 ( 1.799 Gb) 64.36% 0.02%
PAGE_EXECUTE_READ 183 0`1212e000 ( 289.180 Mb) 10.10% 0.00%
PAGE_READONLY 941 0`082f9000 ( 130.973 Mb) 4.57% 0.00%
PAGE_READWRITE|PAGE_WRITECOMBINE 24 0`03aad000 ( 58.676 Mb) 2.05% 0.00%
PAGE_EXECUTE_READWRITE 131 0`00bcc000 ( 11.797 Mb) 0.41% 0.00%
PAGE_READWRITE|PAGE_GUARD 87 0`00191000 ( 1.566 Mb) 0.05% 0.00%
PAGE_NOACCESS 399 0`0018f000 ( 1.559 Mb) 0.05% 0.00%
<unknown> 1 0`00004000 ( 16.000 kb) 0.00% 0.00%
PAGE_EXECUTE 2 0`00003000 ( 12.000 kb) 0.00% 0.00%
PAGE_WRITECOPY 1 0`00002000 ( 8.000 kb) 0.00% 0.00%
--- Largest Region by Usage ----------- Base Address -------- Region Size ----------
Free a1`9f910000 754`5f4fc000 ( 7.330 Tb)
<unknown> a1`4fc30000 0`17ffc000 ( 383.984 Mb)
Heap a1`1c421000 0`00dff000 ( 13.996 Mb)
Image 0`6c991000 0`0174e000 ( 23.305 Mb)
Stack a1`160c0000 0`000fb000 (1004.000 kb)
Other a1`4e480000 0`00181000 ( 1.504 Mb)
TEB 7f5`fee0c000 0`00002000 ( 8.000 kb)
PEB 7f5`ff14c000 0`00001000 ( 4.000 kb)
为什么空闲块这么大?在摘要中,是否存在内存泄漏的线索?
理论上,一个 64 位进程可以访问 16 exbibytes 的虚拟内存,但现代 CPU 和操作系统通常会限制这一点。 Windows 允许寻址 8 TB。请注意,无论您的计算机中有多少物理内存,所有 64 位进程都有自己的私有 8 TB 虚拟内存块。 <unknown>
块表示通过虚拟分配(以及其他)使用的内存,其中包括 .NET 内存。这是您提到的 1.835 GB 虚拟内存。 1 TB 是 1,024 GB,这只是 8 TB 总可寻址虚拟内存的一小部分。结果,空闲块仍然非常大。这在 64 位程序中非常典型,因为使用数 TB 内存的程序并不多。尝试使用 32 位程序进行此实验,以查看截然不同的结果。
您描述的是一个常见问题,不仅适用于测试人员,也适用于开发人员。在了解整个画面之前,您永远不知道有多少内存是真正空闲的。
我用酒吧老板的比喻来解释这个问题。那个酒保订婚参加一个非常盛大的聚会。他没有足够的眼镜,所以他联系了一家眼镜租赁公司。那个玻璃租赁公司有8000个眼镜要租
酒保点了 2000 杯。聚会开始时,所有的杯子都是空的。但是谁能给这个说法呢?从眼镜租赁公司的角度来看,2000 副眼镜已经用完了——不能再订购了。所以玻璃租赁公司认为它们是 "in use",而酒保认为它们是 "not in use"。
派对开始,服务员接单,酒保调酒。在某个时间点,酒保给了服务员 500 杯。在他看来,1500杯是空的,500杯是满的。
但是,这 500 杯可能还不够。有些客人可能已经非常口渴,已经喝光了杯子。所以实际上,这500个杯子中可能有200个是空的,只有300个杯子是满的。
所以,这取决于你问谁:
- 眼镜租赁公司:租了2000副眼镜
- 酒保:500 杯已送出未归还
- 客人:300杯满了
程序的虚拟内存也是一样,只是执行者不同:
- 玻璃租赁公司=操作系统
- barkeeper = 堆管理器,例如 .NET 堆管理器或 Windows 堆管理器(C++ 和其他)
- 来宾 = 应用程序逻辑
用!address
你问操作系统。如果它说 "I have given 1.8 GB to .NET",那么这是一个有助于理解现实的正确陈述。
但是,您不知道.NET 说的是什么。你需要问它。 !dumpheap -stat
是一个合适的问题。该答案将包括一些关于 Free
.
类型对象的陈述
即使您看到 byte[1000000]
,它被 .NET 视为 "in use",从应用程序逻辑的角度来看,这 1 MB 可能部分为空。如果 byte[]
是一个缓冲区,通常会有一些 int length
属性 告诉应用程序实际使用了多少缓冲区。其余的可以认为是 "free".
结论
您需要在正确的级别上提出正确的问题,以确定是否正在使用内存。
Why the Free block is so large?
因为这是玻璃租赁公司的回答。有很多眼镜可以租(还剩6000个)
Within the summary, any clue where memory leak could be?
增长表明泄漏。您无法从单个快照中识别它。您需要随着时间的推移对其进行监控。哪个内存在增长,哪个没有增长?
不幸的是,无法从给定的信息中得出任何结论。
我有一个使用 C# 和 WPF 构建的应用程序,运行 Windows 8。C# 代码使用 Interop 调用外部 C++ dll。经过一系列的操作,内存占用接近2GB,怀疑是内存泄漏。我创建了进程转储并尝试使用 WinDbg 分析它,通过命令 !address -summary
,我收到以下消息:
--- Usage Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
Free 482 7ff`4d11b000 ( 7.997 Tb) 99.97%
<unknown> 1208 0`7573d000 ( 1.835 Gb) 65.64% 0.02%
Heap 1664 0`1e1ab000 ( 481.668 Mb) 16.82% 0.01%
Image 1294 0`19f98000 ( 415.594 Mb) 14.52% 0.00%
Stack 261 0`053ce000 ( 83.805 Mb) 2.93% 0.00%
Other 23 0`001d8000 ( 1.844 Mb) 0.06% 0.00%
TEB 87 0`000ae000 ( 696.000 kb) 0.02% 0.00%
PEB 1 0`00001000 ( 4.000 kb) 0.00% 0.00%
--- Type Summary (for busy) ------ RgnCount ----------- Total Size -------- %ofBusy %ofTotal
MEM_PRIVATE 2634 0`922ae000 ( 2.284 Gb) 81.69% 0.03%
MEM_IMAGE 1787 0`1b38d000 ( 435.551 Mb) 15.21% 0.01%
MEM_MAPPED 90 0`05807000 ( 88.027 Mb) 3.07% 0.00%
--- State Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
MEM_FREE 509 7ff`4d1ae000 ( 7.997 Tb) 99.97%
MEM_COMMIT 3346 0`9205e000 ( 2.282 Gb) 81.61% 0.03%
MEM_RESERVE 1165 0`20de4000 ( 525.891 Mb) 18.37% 0.01%
--- Protect Summary (for commit) - RgnCount ----------- Total Size -------- %ofBusy %ofTotal
PAGE_READWRITE 1577 0`73295000 ( 1.799 Gb) 64.36% 0.02%
PAGE_EXECUTE_READ 183 0`1212e000 ( 289.180 Mb) 10.10% 0.00%
PAGE_READONLY 941 0`082f9000 ( 130.973 Mb) 4.57% 0.00%
PAGE_READWRITE|PAGE_WRITECOMBINE 24 0`03aad000 ( 58.676 Mb) 2.05% 0.00%
PAGE_EXECUTE_READWRITE 131 0`00bcc000 ( 11.797 Mb) 0.41% 0.00%
PAGE_READWRITE|PAGE_GUARD 87 0`00191000 ( 1.566 Mb) 0.05% 0.00%
PAGE_NOACCESS 399 0`0018f000 ( 1.559 Mb) 0.05% 0.00%
<unknown> 1 0`00004000 ( 16.000 kb) 0.00% 0.00%
PAGE_EXECUTE 2 0`00003000 ( 12.000 kb) 0.00% 0.00%
PAGE_WRITECOPY 1 0`00002000 ( 8.000 kb) 0.00% 0.00%
--- Largest Region by Usage ----------- Base Address -------- Region Size ----------
Free a1`9f910000 754`5f4fc000 ( 7.330 Tb)
<unknown> a1`4fc30000 0`17ffc000 ( 383.984 Mb)
Heap a1`1c421000 0`00dff000 ( 13.996 Mb)
Image 0`6c991000 0`0174e000 ( 23.305 Mb)
Stack a1`160c0000 0`000fb000 (1004.000 kb)
Other a1`4e480000 0`00181000 ( 1.504 Mb)
TEB 7f5`fee0c000 0`00002000 ( 8.000 kb)
PEB 7f5`ff14c000 0`00001000 ( 4.000 kb)
为什么空闲块这么大?在摘要中,是否存在内存泄漏的线索?
理论上,一个 64 位进程可以访问 16 exbibytes 的虚拟内存,但现代 CPU 和操作系统通常会限制这一点。 Windows 允许寻址 8 TB。请注意,无论您的计算机中有多少物理内存,所有 64 位进程都有自己的私有 8 TB 虚拟内存块。 <unknown>
块表示通过虚拟分配(以及其他)使用的内存,其中包括 .NET 内存。这是您提到的 1.835 GB 虚拟内存。 1 TB 是 1,024 GB,这只是 8 TB 总可寻址虚拟内存的一小部分。结果,空闲块仍然非常大。这在 64 位程序中非常典型,因为使用数 TB 内存的程序并不多。尝试使用 32 位程序进行此实验,以查看截然不同的结果。
您描述的是一个常见问题,不仅适用于测试人员,也适用于开发人员。在了解整个画面之前,您永远不知道有多少内存是真正空闲的。
我用酒吧老板的比喻来解释这个问题。那个酒保订婚参加一个非常盛大的聚会。他没有足够的眼镜,所以他联系了一家眼镜租赁公司。那个玻璃租赁公司有8000个眼镜要租
酒保点了 2000 杯。聚会开始时,所有的杯子都是空的。但是谁能给这个说法呢?从眼镜租赁公司的角度来看,2000 副眼镜已经用完了——不能再订购了。所以玻璃租赁公司认为它们是 "in use",而酒保认为它们是 "not in use"。
派对开始,服务员接单,酒保调酒。在某个时间点,酒保给了服务员 500 杯。在他看来,1500杯是空的,500杯是满的。
但是,这 500 杯可能还不够。有些客人可能已经非常口渴,已经喝光了杯子。所以实际上,这500个杯子中可能有200个是空的,只有300个杯子是满的。
所以,这取决于你问谁:
- 眼镜租赁公司:租了2000副眼镜
- 酒保:500 杯已送出未归还
- 客人:300杯满了
程序的虚拟内存也是一样,只是执行者不同:
- 玻璃租赁公司=操作系统
- barkeeper = 堆管理器,例如 .NET 堆管理器或 Windows 堆管理器(C++ 和其他)
- 来宾 = 应用程序逻辑
用!address
你问操作系统。如果它说 "I have given 1.8 GB to .NET",那么这是一个有助于理解现实的正确陈述。
但是,您不知道.NET 说的是什么。你需要问它。 !dumpheap -stat
是一个合适的问题。该答案将包括一些关于 Free
.
即使您看到 byte[1000000]
,它被 .NET 视为 "in use",从应用程序逻辑的角度来看,这 1 MB 可能部分为空。如果 byte[]
是一个缓冲区,通常会有一些 int length
属性 告诉应用程序实际使用了多少缓冲区。其余的可以认为是 "free".
结论
您需要在正确的级别上提出正确的问题,以确定是否正在使用内存。
Why the Free block is so large?
因为这是玻璃租赁公司的回答。有很多眼镜可以租(还剩6000个)
Within the summary, any clue where memory leak could be?
增长表明泄漏。您无法从单个快照中识别它。您需要随着时间的推移对其进行监控。哪个内存在增长,哪个没有增长? 不幸的是,无法从给定的信息中得出任何结论。