用 .NET Memory Profiler 跟踪.net 应用内存使用情况--基本应用篇-阿里云开发者社区

开发者社区> 余二五> 正文

用 .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,如需转载请自行联系原作者

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
WPF:使用Json.NET在TreeView中树形显示JSON数据
原文  WPF:使用Json.NET在TreeView中树形显示JSON数据 据 读者可以参考这个开源的可以树形显示XML和JSON的工具: Mgen Object 603:XML/JSON树形显示小工具 或者一个更大的开源工程(构建和分析HTTP并支持XML及JSON的树形显示): Mgen Bluckbadda   效果如下: (每一个项目中的左侧黑字是数据的值,右侧灰字是数据的类型。
1665 0
Web系统下Office文档的处理 之OpenXml应用分类资料
本文对常见的处理参考和工具提供一个简要的参考。 对于实际的解决方案参考如下地址(有很多的分类知识) http://blogs.msdn.com/b/ericwhite/archive/2008/10/20/eric-white-s-blog-s-table-of-contents.
956 0
Reflection.Emit的使用场景、工具包及示例总结
最近处理一个业务需要动态的生成一些业务模型和库,使用到了Emit的处理,相关的资料整理一下供参考。 Reflection.Emit目的 使用的场景: 应用中自定义一个自己的语言 运行中动态的创建类型、模块等,同时又需要提高效率(可以动态编译一次,然后就不用再处理了) 延迟绑定对象的使用,在和Office这类的软件时会用到 动态插件系统等 … System.
967 0
前端内存优化的探索与实践
标注是地图最基本的元素之一,标明了地图每个位置或线路的名称。在地图 JSAPI 中,标注的展示效果及性能也是需要重点解决的问题。
397 0
《区块链DAPP开发入门、代码实现、场景应用》笔记4——Ethereum Wallet中部署合约
账号创建完成之后,账号余额是0,但是部署合约是需要消耗GAS的,因此需要获取一定的以太币才能够继续本次实现。
1510 0
+关注
20382
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
《2021云上架构与运维峰会演讲合集》
立即下载
《零基础CSS入门教程》
立即下载
《零基础HTML入门教程》
立即下载