用 .NET Memory Profiler 跟踪.net 应用内存使用情况--基本应用篇

简介:

.net 框架号称永远不会发生内存泄漏,原因是其引入了内存回收的机制。但实际应用中,往往我们分配了对象但没有释放指向该对象的引用,导致对象永远无法释放。最常见的情况就是给对象添加了事件处理函数,但当不再使用该对象时却没有将该函数从对象的事件handler中减掉。另外如果分配了非托管内存,而没有手工释放,GC同样无能为力。所以当.net应用发生内存泄漏后如何跟踪应用的内存使用情况,定位到程序设计中的缺陷显得非常重要。本文将介绍通过.NET Memory Profiler来跟踪.net应用的内存泄漏,为定位.net应用内存问题提供一个解决途径。

     .NET Memory Profiler是一款强大的.net 内存跟踪和优化工具。该工具目前可以对一下4种.net应用进行内存跟踪。

  • 基本应用 例如winform, console application等

  • ASP.net 应用

  • WPF应用

  • Window 服务

     本篇将通过对以下三种内存的跟踪来阐述如何使用该工具对基本.net应用程序进行内存的跟踪。三种内存包括:

  • 托管内存

  • 线程托管内存

  • 非托管内存

 

在开始之前,先需要建立环境。
       我采用.NET Memory Profiler V3.1.307 版本进行测试。安装完后需要新建一个项目,由于我们需要测
.net基本应用,所以新建项目时选择Standalone application. 点击next后,输入要测试的.net 应用的路径和参数。
然后按下 finish.项目就建立完成了。

       测试程序是我编写的,编译后生成TestMemorySize.exe 这个控制台应用程序。下载地址

       代码如下

 

     主程序等待用户输入,输入m,t,u 分别是增加托管内存,创建一个自动增加托管内存的线程,增加非托管内存。
输入d,释放主线程创建的托管内存对象。

using System;
using System.Collections.Generic;
using System.Text;

namespace TestMemorySize
{
    
class Program
    {
        
static void Main(string[] args)
        {
            MemoryInc memoryInc 
= new MemoryInc();

            
while (true)
            {
                
long memorysize = System.Diagnostics.Process.GetCurrentProcess().PagedMemorySize64;

                Console.WriteLine(
string.Format("PagedMemorySize:{0}MB", memorysize / (1024*1024)));
                Console.WriteLine(
string.Format("ManagedMemIncTimes:{0}", memoryInc.ManagedMemIncTimes));
                Console.WriteLine(
string.Format("UnmanagedMemIncTimes:{0}", memoryInc.UnmanagedMemIncTimes));

                String cmd 
= Console.ReadLine();

                
switch (cmd)
                {
                    
case "d":
                        memoryInc 
= new MemoryInc();
                        GC.Collect();
                        
break;
                    
case "m":
                        memoryInc.IncManagedMemory();
                        
break;
                    
case "u":
                        memoryInc.IncUnmanagedMemory();
                        
break;
                    
case "t":
                        MemoryLeakThread thread 
= new MemoryLeakThread();
                        
break;
                    
case "l":
                        
break;
                }
            }
        }
    }
}

 

MemoryInc 是一个增加托管内存和非托管内存的类。

 

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;

namespace TestMemorySize
{
    
class MemoryInc
    {
        
int _ManagedMemIncTimes = 0;
        
int _UnmanagedMemIncTimes = 0;

        List
<byte[]> _ManagedMemory = new List<byte[]>();
        LinkedList
<IntPtr> _UnmanagedMemory = new LinkedList<IntPtr>();

        
/// <summary>
        
/// Managed memory increase times
        
/// </summary>
        public int ManagedMemIncTimes
        {
            
get
            {
                
return _ManagedMemIncTimes;
            }
        }

        
/// <summary>
        
/// Unmanaged memory increase times
        
/// </summary>
        public int UnmanagedMemIncTimes
        {
            
get
            {
                
return _UnmanagedMemIncTimes;
            }
        }

        
/// <summary>
        
/// Increase managed memory
        
/// </summary>
        public void IncManagedMemory()
        {
            _ManagedMemIncTimes
++;

            _ManagedMemory.Add(
new byte[1024 * 1024 * _ManagedMemIncTimes]);
        }

        
/// <summary>
        
/// Increase unmanaged memory
        
/// </summary>
        public void IncUnmanagedMemory()
        {
            _UnmanagedMemIncTimes
++;

            _UnmanagedMemory.AddLast(Marshal.AllocCoTaskMem(
1024 * 1024 * _UnmanagedMemIncTimes));
        }
    }
}

 

MemoryLeakThread 这个线程没30秒增加1M的托管内存占用。

 

 

ExpandedBlockStart.gif
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace TestMemorySize
{
    
class MemoryLeakThread
    {
        Thread _Thread;
        
byte[] _Buf;
        
int _Times = 0;

        
private void ThreadProc()
        {
            
while (true)
            {
                _Times
++;
                _Buf 
= new byte[_Times * 1024 * 1024];
                Thread.Sleep(
30 * 1000);
            }
        }

        
public MemoryLeakThread()
        {
            _Thread 
= new Thread(new ThreadStart(ThreadProc));
            _Thread.IsBackground 
= true;
            _Thread.Start();
        }
    }
}


     准备就绪,下面就开始体验了。

1、托管内存的跟踪
       菜单中选择Profiler->Start 启动TestMemorySize.exe,然后输入m 并回车,这是分配了1M的托管内存。
在菜单中选择Profiler->Collect Heap Shapshot. 这是就可以看到堆中的所有对象了。

 

 

从这个界面我们看到虽然列出了对象的列表,但只有类型和大小等信息,却没有对象的名称以及分配过程
信息,这样怎么定位那块内存没有被释放啊?不要着急,.NET Memory Profiler还是比较强大的,让我们继续往下
前进。

 

双击选中的对象后进入对象所占用的堆的详细信息

 

 

再双击选中行,这时我们就可以看到对象的名称和分配堆栈的情况了。是不是很兴奋?终于找到是哪个家伙在捣蛋了。

 

 

2、线程中创建的托管内存的跟踪
       线程中创建的托管内存跟踪方法和第1节介绍的方法基本是一样的。启动TestMemorySize.exe后输入t 并回车,创建一个
吃内存的线程。下面步骤都相同了。

 

 

 

3、非托管内存的跟踪
       要跟踪非托管内存需要做一个设置:选择菜单中view->Project Property Pages,按下图进行设置。

 

设置好后启动TestMemorySize.exe后输入u 并回车,创建1M的非托管内存。下面步骤相同。

 

 

 

 

非托管内存无法看到对象的名称,但可以看到内存的申请过程,这对于定位内存问题已经提供了很大的帮助。

现在我们再输入m 回车,创建1M的托管内存,然后输入d 回车,这时我们可以发现memoryInc对象申请的托管内存已经被释放掉,
但非托管内存依然存在,内存在这里泄漏了!

这个工具还可以帮助我们计算出托管对象在堆中实际占用的内存大小,这也是一个很实用的功能,我们可以发现实际的占用大小
要比我们设计的大小略大,这是因为我们设计的类及其成员都是从一些基类中继承,这些基类的数据占用了一些内存造成。











本文转自 yuanzhitang 51CTO博客,原文链接:http://blog.51cto.com/yuanzhitang/1882170,如需转载请自行联系原作者
目录
相关文章
|
Arthas 监控 Java
Arthas memory(查看 JVM 内存信息)
Arthas memory(查看 JVM 内存信息)
978 6
|
10月前
|
SQL 缓存 安全
深度理解 Java 内存模型:从并发基石到实践应用
本文深入解析 Java 内存模型(JMM),涵盖其在并发编程中的核心作用与实践应用。内容包括 JMM 解决的可见性、原子性和有序性问题,线程与内存的交互机制,volatile、synchronized 和 happens-before 等关键机制的使用,以及在单例模式、线程通信等场景中的实战案例。同时,还介绍了常见并发 Bug 的排查与解决方案,帮助开发者写出高效、线程安全的 Java 程序。
521 0
|
存储 缓存 监控
如何使用内存监控工具来优化 Node.js 应用的性能
需要注意的是,不同的内存监控工具可能具有不同的功能和特点,在使用时需要根据具体工具的要求和操作指南进行正确使用和分析。
751 158
|
JavaScript
如何使用内存快照分析工具来分析Node.js应用的内存问题?
需要注意的是,不同的内存快照分析工具可能具有不同的功能和操作方式,在使用时需要根据具体工具的说明和特点进行灵活运用。
705 159
|
Arthas 监控 Java
Arthas mc(Memory Compiler/内存编译器 )
Arthas mc(Memory Compiler/内存编译器 )
543 6
|
C# Android开发 iOS开发
2025年全面的.NET跨平台应用框架推荐
2025年全面的.NET跨平台应用框架推荐
760 23
|
开发框架 .NET PHP
网站应用项目如何选择阿里云服务器实例规格+内存+CPU+带宽+操作系统等配置
对于使用阿里云服务器的搭建网站的用户来说,面对众多可选的实例规格和配置选项,我们应该如何做出最佳选择,以最大化业务效益并控制成本,成为大家比较关注的问题,如果实例、内存、CPU、带宽等配置选择不合适,可能会影响到自己业务在云服务器上的计算性能及后期运营状况,本文将详细解析企业在搭建网站应用项目时选购阿里云服务器应考虑的一些因素,以供参考。
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
614 1
|
存储 缓存 数据安全/隐私保护
DMA(Direct Memory Access):直接内存访问
DMA(Direct Memory Access)是一种允许外设直接与内存进行数据传输的技术,无需 CPU 干预。它通过减轻 CPU 负担、提高数据传输效率来提升系统性能。DMA 的工作模式包括直接模式和 FIFO 模式,数据传输方式有单字传送和块传送,寻址模式有增量寻址和非增量寻址。通过缓存一致性协议、同步机制、数据校验和合理的内存管理,DMA 确保了数据在内存中的一致性和完整性。
|
10月前
|
存储
阿里云轻量应用服务器收费标准价格表:200Mbps带宽、CPU内存及存储配置详解
阿里云香港轻量应用服务器,200Mbps带宽,免备案,支持多IP及国际线路,月租25元起,年付享8.5折优惠,适用于网站、应用等多种场景。
3118 0