我的编程空间,编程开发者的网络收藏夹
学习永远不晚

NET中怎么排查内存持续增长问题

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

NET中怎么排查内存持续增长问题

这期内容当中小编将会给大家带来有关NET中怎么排查内存持续增长问题,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。

    1、服务端代码,只提供GetFile操作,返回相对较大的内容,便于快速看到内存持续增长的过程。

class Program     {         static void Main(string[] args)         {             using (ServiceHost host = new ServiceHost(typeof(FileImp)))             {                 host.AddServiceEndpoint(typeof(IFile), new WSHttpBinding(), "http://127.0.0.1:9999/FileService");                 if (host.Description.Behaviors.Find<ServiceMetadataBehavior>() == null)                 {                     ServiceMetadataBehavior behavior = new ServiceMetadataBehavior();                     behavior.HttpGetEnabled = true;                     behavior.HttpGetUrl = new Uri("http://127.0.0.1:9999/FileService/metadata");                     host.Description.Behaviors.Add(behavior);                 }                 host.Opened += delegate                  {                      Console.WriteLine("FileService已经启动,按任意键终止服务!");                  };                 host.Open();                 Console.Read();             }         }     }      class FileImp : IFile     {         static byte[] _fileContent = new byte[1024 * 8];          public byte[] GetFile(string fileName)         {             int loginID = OperationContext.Current.IncomingMessageHeaders.GetHeader<int>("LoginID", string.Empty);             Console.WriteLine(string.Format("调用者ID:{0}", loginID));             return _fileContent;         }     }

    2、客户端代码,循环调用GetFile操作,在调用前给消息头添加一些登录信息。另外为了避免垃圾回收机制执行的不确定性对内存增长的干扰,在每次调用完毕后,强制启动垃圾回收机制,对所有代进行垃圾回收,确保增长的内存都是可到达,无法对其进行回收。

class Program     {         static void Main(string[] args)         {             int callCount = 0;             int loginID = 0;             while (true)             {                 using (ChannelFactory<IFile> channelFactory =                     new ChannelFactory<IFile>(new WSHttpBinding(), "http://127.0.0.1:9999/FileService"))                 {                     IFile fileProxy = channelFactory.CreateChannel();                     using (fileProxy as IDisposable)                     {                         //OperationContext.Current = new OperationContext(fileProxy as IContextChannel);                         OperationContextScope scope = new OperationContextScope(fileProxy as IContextChannel);                         var loginIDHeadInfo = MessageHeader.CreateHeader("LoginID", string.Empty, ++loginID);                         OperationContext.Current.OutgoingMessageHeaders.Add(loginIDHeadInfo);                         byte[] fileContent = fileProxy.GetFile(string.Empty);                     }                 }                 GC.Collect();//强制启动垃圾回收                 Console.WriteLine(string.Format("调用次数:{0}", ++callCount));             }         }     }

二、分析排查

     要解决内存持续增长的问题,首先需要定位问题,才能做相应的修复。对于逻辑简单的代码,可以简单直接通过排除法来定位问题代码所在,对于错综复杂的代 码,就需要耗费一定时间了。当然除了排除法,还可以借助内存检测工具来快速定位问题代码。对于.net平台,微软提供.net辅助工具CLR  Profiler帮助我们的性能测试人员以及研发人员,找到内存没有及时回收,占着内存不释放的方法。监测客户端程序运行的结果如下:

NET中怎么排查内存持续增长问题

     从上图可看到OperationContextScope对象占用了98%的内存,当前OperationContextScope对象持有256个 OperationContextScope对象的引用,这些OperationContextScope对象总共持有258个 OperationContext的引用,每个OperationContext对象持有客户端代理的相关对象引用,导致每个客户端代理产生的内存在使用 完毕后都无法得到释放。

三、问题解决

    OperationContextScope类主要作用是创建一个块,其中 OperationContext  对象在范围之内。也就是说创建一个基于OperationContext 的上下文范围,在这范围内共享一个相同的OperationContext对 象。这种上下文的特性是支持嵌套的,即一个大的上下文范围内可以有若干个小的上下文范围,而且不会造成相互不干扰。所以如果没显式调用该对象的 Dispose方法结束当前上下文恢复前一上下文,再利用OperationContextScope类创建新的上下文,就会一直嵌套下去。所以在这里应 该要显式调用Dispose方法结束当前OperationContextScope上下文范围,这样可以解决内存持续增长的问题了。

class Program     {         static void Main(string[] args)         {             int callCount = 0;             int loginID = 0;             while (true)             {                 using (ChannelFactory<IFile> channelFactory =                     new ChannelFactory<IFile>(new WSHttpBinding(), "http://127.0.0.1:9999/FileService"))                 {                     IFile fileProxy = channelFactory.CreateChannel();                     using (fileProxy as IDisposable)                     {                         //OperationContext.Current = new OperationContext(fileProxy as IContextChannel);                         using (OperationContextScope scope = new OperationContextScope(fileProxy as IContextChannel))                         {                             var loginIDHeadInfo = MessageHeader.CreateHeader("LoginID", string.Empty, ++loginID);                             OperationContext.Current.OutgoingMessageHeaders.Add(loginIDHeadInfo);                         }                         byte[] fileContent = fileProxy.GetFile(string.Empty);                     }                 }                 GC.Collect();//强制启动垃圾回收                 Console.WriteLine(string.Format("调用次数:{0}", ++callCount));             }         }     }

 四、问题根源

    OperationContextScope为什么能持有大量的OperationContext引用?从CLR  Profiler工具获取的结果中可以看到OperationContextScope对象通过其内部OperationContextScope对象来 持有大量OperationContext对象引用,可以推断该类应该有一个OperationContextScope类型的字段。下面看一下OperationContextScope类的源码。

public sealed class OperationContextScope : IDisposable     {         [ThreadStatic]         static OperationContextScope currentScope;           OperationContext currentContext;         bool disposed;         readonly OperationContext originalContext = OperationContext.Current;         readonly OperationContextScope originalScope = OperationContextScope.currentScope;         readonly Thread thread = Thread.CurrentThread;           public OperationContextScope(IContextChannel channel)         {             this.PushContext(new OperationContext(channel));         }           public OperationContextScope(OperationContext context)         {             this.PushContext(context);         }           public void Dispose()         {             if (!this.disposed)             {                 this.disposed = true;                 this.PopContext();             }         }           void PushContext(OperationContext context)         {             this.currentContext = context;             OperationContextScope.currentScope = this;             OperationContext.Current = this.currentContext;         }           void PopContext()         {             if (this.thread != Thread.CurrentThread)                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxInvalidContextScopeThread0)));               if (OperationContextScope.currentScope != this)                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxInterleavedContextScopes0)));               if (OperationContext.Current != this.currentContext)                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxContextModifiedInsideScope0)));               OperationContextScope.currentScope = this.originalScope;             OperationContext.Current = this.originalContext;               if (this.currentContext != null)                 this.currentContext.SetClientReply(null, false);         }     }

     当前的上下文对象由线程***的静态字段currentScope持有,其实例字段originalScope保持前一上下文对象的引用,如果使用完毕后不 结束当前上下文范围,就会一直嵌套下去,导致所有OperationContext对象都保持可到达,垃圾回收机制无法进行回收,从而使得内存持续增长, 直到内存溢出。

    NET中怎么排查内存持续增长问题

上述就是小编为大家分享的NET中怎么排查内存持续增长问题了,如果刚好有类似的疑惑,不妨参照上述分析进行理解。如果想知道更多相关知识,欢迎关注编程网行业资讯频道。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

NET中怎么排查内存持续增长问题

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

NET中怎么排查内存持续增长问题

这期内容当中小编将会给大家带来有关NET中怎么排查内存持续增长问题,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。 1、服务端代码,只提供GetFile操作,返回相对较大的内容,便于快速看到内存持续增
2023-06-17

golang内存持续增长怎么解决

在Go语言中,内存持续增长的问题通常是由于内存泄漏或者不合理的内存使用导致的。以下是一些可能的解决方案:检查代码中是否存在内存泄漏的问题。使用工具如Go的pprof包来分析内存使用情况,并查看是否有未释放的资源。确保在不再使用某个对象时及时
2023-10-23

java内存逃逸问题怎么排查

Java内存逃逸问题通常是指在程序中创建的对象在其作用域之外仍然被引用,导致无法被垃圾收集器回收。以下是一些排查Java内存逃逸问题的方法:使用内存分析工具:使用工具如VisualVM、jmap、jstack等来分析内存使用情况。这些工具可
2023-10-23

linux内存泄漏问题怎么排查

要排查Linux中的内存泄漏问题,可以按照以下步骤进行:1. 监控内存使用情况:使用工具如top、free或htop等监控系统的实时内存使用情况,观察内存占用是否逐渐增加,并查看哪个进程占用了大量内存。2. 检查系统日志:查看系统日志文件(
2023-10-21

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录