ICode9

精准搜索请尝试: 精确搜索
首页 > 系统相关> 文章详细

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

2022-04-08 17:01:23  阅读:235  来源: 互联网

标签:-- 托管 System new Profiler 内存 Memory using 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的托管内存占用。

 

 


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对象申请的托管内存已经被释放掉,
但非托管内存依然存在,内存在这里泄漏了!

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

 

到此如何跟踪基本.net应用的内存问题就介绍完毕。有时间再谢谢怎么跟踪ASP.NET应用的内存问题。
这一篇本来上午就要发出来,都快写完了,IE 崩溃!抓狂!

下午又重新写了一遍,郁闷啊。

 

标签:--,托管,System,new,Profiler,内存,Memory,using,net
来源: https://www.cnblogs.com/lzjsky/p/16118178.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有