用 .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,如需转载请自行联系原作者
目录
相关文章
|
1天前
|
算法 Java 数据库连接
.NET 内存管理两种有效的资源释放方式
【10月更文挑战第14天】在 .NET 中,`IDisposable` 接口提供了一种标准机制来释放非托管资源,如文件句柄、数据库连接等。此类资源需手动释放以避免泄漏。实现 `IDisposable` 的类可通过 `Dispose` 方法释放资源。使用 `using` 语句可确保资源自动释放。此外,.NET 的垃圾回收器会自动回收托管对象所占内存,提高程序效率。示例代码展示了如何使用 `MyFileHandler` 类处理文件操作并释放 `FileStream` 资源。
|
1月前
|
存储 网络协议 大数据
一文读懂RDMA: Remote Direct Memory Access(远程直接内存访问)
该文档详细介绍了RDMA(远程直接内存访问)技术的基本原理、主要特点及其编程接口。RDMA通过硬件直接在应用程序间搬移数据,绕过操作系统协议栈,显著提升网络通信效率,尤其适用于高性能计算和大数据处理等场景。文档还提供了RDMA编程接口的概述及示例代码,帮助开发者更好地理解和应用这一技术。
|
1月前
|
存储 运维
.NET开发必备技巧:使用Visual Studio分析.NET Dump,快速查找程序内存泄漏问题!
.NET开发必备技巧:使用Visual Studio分析.NET Dump,快速查找程序内存泄漏问题!
|
2月前
|
数据采集 Rust 安全
Rust在网络爬虫中的应用与实践:探索内存安全与并发处理的奥秘
【8月更文挑战第31天】网络爬虫是自动化程序,用于从互联网抓取数据。随着互联网的发展,构建高效、安全的爬虫成为热点。Rust语言凭借内存安全和高性能特点,在此领域展现出巨大潜力。本文探讨Rust如何通过所有权、借用及生命周期机制保障内存安全;利用`async/await`模型和`tokio`运行时处理并发请求;借助WebAssembly技术处理动态内容;并使用`reqwest`和`js-sys`库解析CSS和JavaScript,确保代码的安全性和可维护性。未来,Rust将在网络爬虫领域扮演更重要角色。
65 1
|
1月前
|
监控 算法 数据可视化
深入解析Android应用开发中的高效内存管理策略在移动应用开发领域,Android平台因其开放性和灵活性备受开发者青睐。然而,随之而来的是内存管理的复杂性,这对开发者提出了更高的要求。高效的内存管理不仅能够提升应用的性能,还能有效避免因内存泄漏导致的应用崩溃。本文将探讨Android应用开发中的内存管理问题,并提供一系列实用的优化策略,帮助开发者打造更稳定、更高效的应用。
在Android开发中,内存管理是一个绕不开的话题。良好的内存管理机制不仅可以提高应用的运行效率,还能有效预防内存泄漏和过度消耗,从而延长电池寿命并提升用户体验。本文从Android内存管理的基本原理出发,详细讨论了几种常见的内存管理技巧,包括内存泄漏的检测与修复、内存分配与回收的优化方法,以及如何通过合理的编程习惯减少内存开销。通过对这些内容的阐述,旨在为Android开发者提供一套系统化的内存优化指南,助力开发出更加流畅稳定的应用。
52 0
|
2月前
|
开发框架 监控 .NET
|
2月前
|
缓存 监控 Android开发
构建高效的Android应用:从内存优化到用户体验
【7月更文挑战第57天】 在竞争激烈的移动市场中,一个高效、流畅且具有优秀用户体验的Android应用是成功的关键。本文将深入探讨如何通过内存管理和界面优化来提升应用性能,包括实用的编程技巧和策略,以及如何利用Android系统提供的工具进行调试和性能监控。读者将学习到如何识别和解决常见的性能瓶颈,以及如何设计出既美观又实用的用户界面。
|
2月前
|
开发框架 JavaScript 前端开发
【App Service】解决 .NET Profiler 报告打开后无数据加载的问题
【App Service】解决 .NET Profiler 报告打开后无数据加载的问题