用 .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,如需转载请自行联系原作者
目录
相关文章
|
15天前
|
开发框架 监控 .NET
【Azure App Service】部署在App Service上的.NET应用内存消耗不能超过2GB的情况分析
x64 dotnet runtime is not installed on the app service by default. Since we had the app service running in x64, it was proxying the request to a 32 bit dotnet process which was throwing an OutOfMemoryException with requests >100MB. It worked on the IaaS servers because we had the x64 runtime install
|
28天前
|
JSON 算法 安全
JWT Bearer 认证在 .NET Core 中的应用
【10月更文挑战第30天】JWT(JSON Web Token)是一种开放标准,用于在各方之间安全传输信息。它由头部、载荷和签名三部分组成,用于在用户和服务器之间传递声明。JWT Bearer 认证是一种基于令牌的认证方式,客户端在请求头中包含 JWT 令牌,服务器验证令牌的有效性后授权用户访问资源。在 .NET Core 中,通过安装 `Microsoft.AspNetCore.Authentication.JwtBearer` 包并配置认证服务,可以实现 JWT Bearer 认证。具体步骤包括安装 NuGet 包、配置认证服务、启用认证中间件、生成 JWT 令牌以及在控制器中使用认证信息
|
1月前
|
存储 弹性计算 算法
前端大模型应用笔记(四):如何在资源受限例如1核和1G内存的端侧或ECS上运行一个合适的向量存储库及如何优化
本文探讨了在资源受限的嵌入式设备(如1核处理器和1GB内存)上实现高效向量存储和检索的方法,旨在支持端侧大模型应用。文章分析了Annoy、HNSWLib、NMSLib、FLANN、VP-Trees和Lshbox等向量存储库的特点与适用场景,推荐Annoy作为多数情况下的首选方案,并提出了数据预处理、索引优化、查询优化等策略以提升性能。通过这些方法,即使在资源受限的环境中也能实现高效的向量检索。
|
1月前
|
编解码 Android开发 UED
构建高效Android应用:从内存优化到用户体验
【10月更文挑战第11天】本文探讨了如何通过内存优化和用户体验改进来构建高效的Android应用。介绍了使用弱引用来减少内存占用、懒加载资源以降低启动时内存消耗、利用Kotlin协程进行异步处理以保持UI流畅,以及采用响应式设计适配不同屏幕尺寸等具体技术手段。
51 2
|
1月前
|
数据库连接 开发者
.NET 内存管理两种有效的资源释放方式
【10月更文挑战第15天】在.NET中,有两种有效的资源释放方式:一是使用`using`语句,适用于实现`IDisposable`接口的对象,如文件流、数据库连接等,能确保资源及时释放,避免泄漏;二是手动调用`Dispose`方法并处理异常,提供更灵活的资源管理方式,适用于复杂场景。这两种方式都能有效管理资源,提高应用性能和稳定性。
|
1月前
|
算法 Java 数据库连接
.NET 内存管理两种有效的资源释放方式
【10月更文挑战第14天】在 .NET 中,`IDisposable` 接口提供了一种标准机制来释放非托管资源,如文件句柄、数据库连接等。此类资源需手动释放以避免泄漏。实现 `IDisposable` 的类可通过 `Dispose` 方法释放资源。使用 `using` 语句可确保资源自动释放。此外,.NET 的垃圾回收器会自动回收托管对象所占内存,提高程序效率。示例代码展示了如何使用 `MyFileHandler` 类处理文件操作并释放 `FileStream` 资源。
|
1月前
|
运维 JavaScript Linux
容器内的Nodejs应用如何获取宿主机的基础信息-系统、内存、cpu、启动时间,以及一个df -h的坑
本文介绍了如何在Docker容器内的Node.js应用中获取宿主机的基础信息,包括系统信息、内存使用情况、磁盘空间和启动时间等。核心思路是将宿主机的根目录挂载到容器,但需注意权限和安全问题。文章还提到了使用`df -P`替代`df -h`以获得一致性输出,避免解析错误。
|
2月前
|
数据采集 JSON API
.NET 3.5 中 HttpWebRequest 的核心用法及应用
【9月更文挑战第7天】在.NET 3.5环境下,HttpWebRequest 类是处理HTTP请求的一个核心组件,它封装了HTTP协议的细节,使得开发者可以方便地发送HTTP请求并接收响应。本文将详细介绍HttpWebRequest的核心用法及其实战应用。
129 6
|
3月前
|
存储 编译器 C语言
【C语言篇】数据在内存中的存储(超详细)
浮点数就采⽤下⾯的规则表⽰,即指数E的真实值加上127(或1023),再将有效数字M去掉整数部分的1。
385 0
|
29天前
|
存储 C语言
数据在内存中的存储方式
本文介绍了计算机中整数和浮点数的存储方式,包括整数的原码、反码、补码,以及浮点数的IEEE754标准存储格式。同时,探讨了大小端字节序的概念及其判断方法,通过实例代码展示了这些概念的实际应用。
61 1