在 WinDbg 中查看有根对象
View rooted objects in WinDbg
我正在尝试使用 WinDbg 调查 .NET 中的内存泄漏。
但是,当我尝试检查 !dumpheap -type
的输出时,我发现很多(如果不是全部)列出的对象都是 "Free" 对象。我想过滤列表以查看是否有任何根目录(有对它们的引用)。
我尝试了以下脚本:
.foreach (t {!dumpheap -mt 0000000091ea94f0 -short}) { .if(!gcroot ${t}) { !mdt ${t} } }
然而,它没有输出任何东西。有没有办法过滤掉 !dumpheap
的输出以仅显示有根对象?
免费"objects"
.NET 使用堆管理器来跟踪内存。这使得分配小于 64 kB 的对象成为可能,其中 64 kB 是 OS 提供的最小内存。
因此,.NET 至少获得 64 kB,然后将其拆分为更小的部分。那些没有被使用的块可以理解为Free
类型的对象。
要更好地了解 Free
个对象,请使用 !dumpheap -stat -type Free
。那些 Free
对象没有根,因为它们实际上不是对象。
但是您还可以看到很多其他对象,包括它们的大小总和。那些可能已经扎根了。
根对象
不幸的是,像 !gcroot
这样的命令没有布尔值 return,因此您需要使用一些棘手的东西。基本的 .foreach
循环已经很不错了。
要获得可比较的 return 值,我们将使用根计数,在以下情况下为 1
:
0:004> !gcroot 02701078
HandleTable:
001f11ec (strong handle)
-> 02701078 System.OutOfMemoryException
Found 1 unique roots (run '!GCRoot -all' to see all roots).
因为数字可以是 1、2、3 等,所以检查 !=0
似乎更可靠。让我们这样开始:
.shell -ci"!gcroot ${t}" find "Found 0"
这只会保留一行"Found 0 unique roots ...",否则什么也不会。
然后让我们通过使用 /pS 1
跳过第一个单词 ("Found") 来最小化输出以保持数字,然后处理一个单词然后跳过其余单词(实际上最多 99 个单词) 使用 /ps 99
:
.foreach /pS 1 /ps 99(word {.shell -ci"!gcroot ${t}" find "Found 0"}) {.echo ${word}}
这将只剩下 0
。
接下来,可以使用$scmp()
来比较字符串:
.if ($scmp("${word}","0")==0) {.echo nothing} .else {.echo something}
整个脚本(为了便于阅读而格式化,删除换行符和缩进):
.foreach (t {!dumpheap -short -mt 70c240b4}) {
.foreach /pS 1 /ps 99 (word {.shell -ci"!gcroot ${t}" find "Found 0"}) {
.if ($scmp("${word}","0")==0){
.echo nothing
} .else {
.echo something
}
}
}
在您的情况下,将 .echo something
替换为 !mdt ${t}
。
PyKd
由于上述脚本难以理解且容易出错,您可能想尝试一下 PyKD。查找 dbgCommand()
以执行调试器命令并将结果作为字符串获取。然后,您可以使用任何 Python 命令对该字符串进行操作,这比 WinDbg 内置函数要容易得多。
我正在尝试使用 WinDbg 调查 .NET 中的内存泄漏。
但是,当我尝试检查 !dumpheap -type
的输出时,我发现很多(如果不是全部)列出的对象都是 "Free" 对象。我想过滤列表以查看是否有任何根目录(有对它们的引用)。
我尝试了以下脚本:
.foreach (t {!dumpheap -mt 0000000091ea94f0 -short}) { .if(!gcroot ${t}) { !mdt ${t} } }
然而,它没有输出任何东西。有没有办法过滤掉 !dumpheap
的输出以仅显示有根对象?
免费"objects"
.NET 使用堆管理器来跟踪内存。这使得分配小于 64 kB 的对象成为可能,其中 64 kB 是 OS 提供的最小内存。
因此,.NET 至少获得 64 kB,然后将其拆分为更小的部分。那些没有被使用的块可以理解为Free
类型的对象。
要更好地了解 Free
个对象,请使用 !dumpheap -stat -type Free
。那些 Free
对象没有根,因为它们实际上不是对象。
但是您还可以看到很多其他对象,包括它们的大小总和。那些可能已经扎根了。
根对象
不幸的是,像 !gcroot
这样的命令没有布尔值 return,因此您需要使用一些棘手的东西。基本的 .foreach
循环已经很不错了。
要获得可比较的 return 值,我们将使用根计数,在以下情况下为 1
:
0:004> !gcroot 02701078
HandleTable:
001f11ec (strong handle)
-> 02701078 System.OutOfMemoryException
Found 1 unique roots (run '!GCRoot -all' to see all roots).
因为数字可以是 1、2、3 等,所以检查 !=0
似乎更可靠。让我们这样开始:
.shell -ci"!gcroot ${t}" find "Found 0"
这只会保留一行"Found 0 unique roots ...",否则什么也不会。
然后让我们通过使用 /pS 1
跳过第一个单词 ("Found") 来最小化输出以保持数字,然后处理一个单词然后跳过其余单词(实际上最多 99 个单词) 使用 /ps 99
:
.foreach /pS 1 /ps 99(word {.shell -ci"!gcroot ${t}" find "Found 0"}) {.echo ${word}}
这将只剩下 0
。
接下来,可以使用$scmp()
来比较字符串:
.if ($scmp("${word}","0")==0) {.echo nothing} .else {.echo something}
整个脚本(为了便于阅读而格式化,删除换行符和缩进):
.foreach (t {!dumpheap -short -mt 70c240b4}) {
.foreach /pS 1 /ps 99 (word {.shell -ci"!gcroot ${t}" find "Found 0"}) {
.if ($scmp("${word}","0")==0){
.echo nothing
} .else {
.echo something
}
}
}
在您的情况下,将 .echo something
替换为 !mdt ${t}
。
PyKd
由于上述脚本难以理解且容易出错,您可能想尝试一下 PyKD。查找 dbgCommand()
以执行调试器命令并将结果作为字符串获取。然后,您可以使用任何 Python 命令对该字符串进行操作,这比 WinDbg 内置函数要容易得多。