使用 NetBeans Profiler 5.5
内存性能分析的不完全统计数据收集
Profiler 提供的每个类的已分配对象的总数(在“仅记录对象创建”和“记录对象创建和垃圾回收”两种模式下)是准确数字,而其他数字(如对象总大小)和反向调用图形缺省情况下都是以统计方式获得的。这是 Profiler 中提供的众多性能改进措施之一。
收集统计数据的工作方式如下:每种分析的类型(类或数组)都具有一个关联的计数器,其初始值为 10(可以在“分析内存使用情况”命令对话框或定制内存性能分析中更改缺省值)。每分配一个给定类的实例,计数器的值就会递减一次。当它达到零时,将确定已分配对象的大小,进行栈跟踪并再次为计数器分配初始值。Profiler 显示的对象分配反向调用图形就是根据这些栈跟踪绘制的,而 Profiler 报告的对象总大小只是进行了栈跟踪的那些对象的总大小。
还请注意,在分配调用图形中看到的方法调用次数与在 CPU 性能分析中看到的方法调用次数不同。在进行内存性能分析时,这些次数并不是实际调用给定方法的次数,而是在给定上下文的栈上发现此方法时进行的栈跟踪次数。因此,您可能会发现 main 应用程序方法包含多个调用。事实上,如果应用程序是单线程的,则为 main 方法显示的“调用”总数与进行的栈跟踪总数是相同的。
当然,使用统计数据收集时,其中的某些调用路径的相关信息就会丢失。然而,实际确定最活跃的对象分配位置和调用路径通常是很重要的。即使使用上述统计栈抽样的方法,这些信息也是很明显的。如果要收集有关调用路径的确切信息,只需将栈抽样间隔值设置为 1 即可。不过,这可能会导致性能显著下降。为了在所收集信息的准确性与性能影响之间找到合理的折衷方案,缺省栈抽样间隔被设置为 10。
另请注意,在此类性能分析中,Profiler 不会严格使用固定的栈抽样间隔。这样做是为了避免不希望的数据关联。例如,如果程序包含一个循环,它在方法 foo() 中连续分配 9 个 X 实例,然后在方法 bar() 中分配一个实例,如果严格遵循每分配 10 次 X 时进行栈抽样,结果可能是只获取了在 bar() 中进行的分配的相关信息,而没有获取在 foo() 中进行的分配的任何相关信息,这将是一个相当失真的结果。要纠正这种问题,Profiler 每次进行栈跟踪后,都会将对象分配计数器设置为与用户定义值略有差异的随机值。如果定义的间隔是 10,Profiler 运行时计数器的实际起始值可能类似于 8、11、10、7、9、12 等。通过这种方式,可以确保最终显示的信息在统计学上是可靠的。
另请参见
