项目中使用微软RDLC生成工作票去打印,但是RDLC存在着严重的内存泄露问题。在生产高峰时期,工人将大量的工作票请求发送到服务器,随着工作票的生成内存就一点点的被吃掉。致使IT部门不得不为一个小小的工作票服务准备一台8G内存的服务器,并且定期的查看服务状态。在生产高峰时期每小时都要重启。

这个内存泄露问题自从VS2005以来就存在,微软声称在2008 SP1中已经修正,但是项目中使用的是2010的程序集版本且问题依然很严重。从微软官方的回复看由于RDLC使用VB进行表达式的计算,加载的VB相关的程序集由于某些原因不被Unload。我想微软在VS2008SP1中修正的应该是这个问题,当然我没有去考证,但是可以肯定的是在VS2010的RDLC中还是有内存泄露的代码存在。
网友还做过测试,如果不使用Expression就不会导致内存泄露,但是我并不想修改太多的程序,如果你的项目刚刚开始或并不复杂,这也是一个办法。参见原文:http://blog.darkthread.net/post-2012-01-12-rdlc-out-of-memory.aspx
于是着手从网上搜索如何查找内存泄露,推荐一个工具给大家。.Net Memory Profile http://memprofiler.com/. 这个工具可以分析出内存中哪些对象已经GC但是没有被成功的移除或移除的不够彻底。通过个这工具分析出LocalReport中的方法被事件或代理对象所引用无法GC。如下图
从上图可以看到LocalReport的身影,这张图中的所有对象全与RDLC有关
。
在网上搜到一牛人用反射解决了ReportViewer的内存泄露问题。参见原贴:http://social.msdn.microsoft.com/Forums/en-US/vsreportcontrols/thread/d21f8b56-3123-4aff-bf84-9cce727bc2ce
于是我参考了这个做法结合.Net Memory Profiler的分析结果开始将LocalReport对象上的事件和代理去掉。虽然这个方法失败了还是把代码贴出来吧,如下:
using System;
using System.Reflection;
using System.Linq;
using System.Windows.Forms;
using Microsoft.Reporting.WinForms;
using Microsoft.Win32;
using System.Collections;
namespace TOG.ProductionOutput.Services
{
public class LocalReportDisposer : IDisposable
{
// Fields
private bool _CollectGarbageOnDispose = false;
private LocalReport localReport;
private bool disposedValue = false;
private const string LOCALREPORT_DATASOURCES = "m_dataSources";
private const string LOCALREPORT_PROCESSINGHOST = "m_processingHost";
private const string PROCESSINGHOST_DATARETRIEVAL = "m_dataRetrieval";
private const string DATARETRIEVAL_SUBREPORTDATACALLBACK = "m_subreportDataCallback";
private const string SUBREPORTDATACALLBACK_TARGET = "_target";
private const string PROCESSINGHOST_EXECUTIONSESSION = "m_executionSession";
private const string EXECUTIONSESSION_COMPILEDREPORT = "__compiledReport";
private const string EXECUTIONSESSION_REPORTSNAPSHOT = "__ReportSnapshot";
private const string DATASOURCES_ONCHANGE = "OnChange";
// Methods
public LocalReportDisposer(LocalReport localReport)
{
if (localReport == null)
{
throw new ArgumentNullException("ReportViewer cannot be null.");
}
this.localReport = localReport;
}
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!this.disposedValue && disposing)
{
//this.TearDownLocalReport();
this.localReport.Dispose();
if (this._CollectGarbageOnDispose)
{
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
}
}
this.disposedValue = true;
}
private void TearDownLocalReport()
{
Type t = this.localReport.GetType();
//localReport.m_dataSources
FieldInfo fi = t.GetField(LOCALREPORT_DATASOURCES, BindingFlags.NonPublic | BindingFlags.Instance);
object dataSources = fi.GetValue(this.localReport);
//remove event from localReport.m_dataSources.Change
ReflectUtil.RemoveEventHandlersFrom(
delegate(Delegate subject) { return subject.Method.Name == DATASOURCES_ONCHANGE; },
dataSources);
//localReport.m_processingHost
fi = t.GetField(LOCALREPORT_PROCESSINGHOST, BindingFlags.NonPublic | BindingFlags.Instance);
object processingHost = fi.GetValue(this.localReport);
//localReport.m_processingHost.dataretrieval
t = processingHost.GetType().BaseType;
fi = t.GetField(PROCESSINGHOST_DATARETRIEVAL, BindingFlags.NonPublic | BindingFlags.Instance);
object dataRetrieval = fi.GetValue(processingHost);
//localReport.m_processingHost.m_dataRetrieval.m_subreportDataCallback
t = dataRetrieval.GetType();
fi = t.GetField(DATARETRIEVAL_SUBREPORTDATACALLBACK, BindingFlags.NonPublic | BindingFlags.Instance);
object subReportDataCallBack = fi.GetValue(dataRetrieval);
//localReport.m_processingHost.m_dataRetrieval.m_subreportDataCallback._target
t = subReportDataCallBack.GetType().BaseType.BaseType;
fi = t.GetField(SUBREPORTDATACALLBACK_TARGET, BindingFlags.NonPublic | BindingFlags.Instance);
fi.SetValue(subReportDataCallBack, null);
t = processingHost.GetType().BaseType;
fi = t.GetField(PROCESSINGHOST_EXECUTIONSESSION, BindingFlags.NonPublic | BindingFlags.Instance);
object executionSession = fi.GetValue(processingHost);
t = executionSession.GetType();
fi = t.GetField(EXECUTIONSESSION_COMPILEDREPORT, BindingFlags.NonPublic | BindingFlags.Instance);
IDisposable report = fi.GetValue(executionSession) as IDisposable;
if (report != null) report.Dispose();
fi = t.GetField(EXECUTIONSESSION_REPORTSNAPSHOT, BindingFlags.NonPublic | BindingFlags.Instance);
report = fi.GetValue(executionSession) as IDisposable;
if (report != null) report.Dispose();
}
// Properties
public bool CollectGarbageOnDispose
{
get
{
return this._CollectGarbageOnDispose;
}
set
{
this._CollectGarbageOnDispose = value;
}
}
}
}using System;using System.Reflection;using System.Linq;using System.Windows.Forms; using Microsoft.Reporting.WinForms; using Microsoft.Win32;using System.Collections;namespace TOG.ProductionOutput.Services{publicclass LocalReportDisposer : IDisposable {// Fields privatebool _CollectGarbageOnDispose = false;private LocalReport localReport;privatebool disposedValue = false;privateconststring LOCALREPORT_DATASOURCES = "m_dataSources";privateconststring LOCALREPORT_PROCESSINGHOST = "m_processingHost";privateconststring PROCESSINGHOST_DATARETRIEVAL = "m_dataRetrieval";privateconststring DATARETRIEVAL_SUBREPORTDATACALLBACK = "m_subreportDataCallback";privateconststring SUBREPORTDATACALLBACK_TARGET = "_target";privateconststring PROCESSINGHOST_EXECUTIONSESSION = "m_executionSession";privateconststring EXECUTIONSESSION_COMPILEDREPORT = "__compiledReport";privateconststring EXECUTIONSESSION_REPORTSNAPSHOT = "__ReportSnapshot";privateconststring DATASOURCES_ONCHANGE = "OnChange";// Methods public LocalReportDisposer(LocalReport localReport) {if (localReport == null) {thrownew ArgumentNullException("ReportViewer cannot be null."); }this.localReport = localReport; }publicvoid Dispose() {this.Dispose(true); GC.SuppressFinalize(this); }protectedvirtualvoid Dispose(bool disposing) {if (!this.disposedValue && disposing) {//this.TearDownLocalReport();this.localReport.Dispose();if (this._CollectGarbageOnDispose) { GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect(); } }this.disposedValue = true; }privatevoid TearDownLocalReport() { Type t = this.localReport.GetType();//localReport.m_dataSourcesFieldInfo fi = t.GetField(LOCALREPORT_DATASOURCES, BindingFlags.NonPublic | BindingFlags.Instance);object dataSources = fi.GetValue(this.localReport);//remove event from localReport.m_dataSources.ChangeReflectUtil.RemoveEventHandlersFrom(delegate(Delegate subject) { return subject.Method.Name == DATASOURCES_ONCHANGE; }, dataSources);//localReport.m_processingHostfi = t.GetField(LOCALREPORT_PROCESSINGHOST, BindingFlags.NonPublic | BindingFlags.Instance);object processingHost = fi.GetValue(this.localReport);//localReport.m_processingHost.dataretrievalt = processingHost.GetType().BaseType; fi = t.GetField(PROCESSINGHOST_DATARETRIEVAL, BindingFlags.NonPublic | BindingFlags.Instance);object dataRetrieval = fi.GetValue(processingHost);//localReport.m_processingHost.m_dataRetrieval.m_subreportDataCallbackt = dataRetrieval.GetType(); fi = t.GetField(DATARETRIEVAL_SUBREPORTDATACALLBACK, BindingFlags.NonPublic | BindingFlags.Instance);object subReportDataCallBack = fi.GetValue(dataRetrieval);//localReport.m_processingHost.m_dataRetrieval.m_subreportDataCallback._targett = subReportDataCallBack.GetType().BaseType.BaseType; fi = t.GetField(SUBREPORTDATACALLBACK_TARGET, BindingFlags.NonPublic | BindingFlags.Instance); fi.SetValue(subReportDataCallBack, null); t = processingHost.GetType().BaseType; fi = t.GetField(PROCESSINGHOST_EXECUTIONSESSION, BindingFlags.NonPublic | BindingFlags.Instance);object executionSession = fi.GetValue(processingHost); t = executionSession.GetType(); fi = t.GetField(EXECUTIONSESSION_COMPILEDREPORT, BindingFlags.NonPublic | BindingFlags.Instance); IDisposable report = fi.GetValue(executionSession) as IDisposable;if (report != null) report.Dispose(); fi = t.GetField(EXECUTIONSESSION_REPORTSNAPSHOT, BindingFlags.NonPublic | BindingFlags.Instance); report = fi.GetValue(executionSession) as IDisposable;if (report != null) report.Dispose(); }// Properties publicbool CollectGarbageOnDispose {get{returnthis._CollectGarbageOnDispose; }set{this._CollectGarbageOnDispose = value; } } }}
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
namespace TOG.ProductionOutput.Services
{
public sealed class ReflectUtil
{
private static BindingFlags PrivatePublicStaticInstance
= BindingFlags.NonPublic | BindingFlags.Public |
BindingFlags.Instance | BindingFlags.Static;
public delegate bool MatchesOnDelegate(Delegate subject);
public static void RemoveEventHandlersFrom(
MatchesOnDelegate matchesOnDelegate, params object[] objectsWithEvents)
{
foreach (object owningObject in objectsWithEvents)
{
foreach (DelegateInfo eventFromOwningObject in GetDelegates(owningObject))
{
foreach (Delegate subscriber in eventFromOwningObject.GetInvocationList())
{
if (matchesOnDelegate(subscriber))
{
EventInfo theEvent = eventFromOwningObject.GetEventInfo();
if(theEvent != null)
RemoveSubscriberEvenIfItsPrivate(theEvent, owningObject, subscriber);
}
}
}
}
}
// You can use eventInfo.RemoveEventHandler(owningObject, subscriber)
// unless it's a private delegate
private static void RemoveSubscriberEvenIfItsPrivate(
EventInfo eventInfo, object owningObject, Delegate subscriber)
{
MethodInfo privateRemoveMethod = eventInfo.GetRemoveMethod(true);
privateRemoveMethod.Invoke(owningObject,
PrivatePublicStaticInstance, null,
new object[] { subscriber }, CultureInfo.CurrentCulture);
}
private static DelegateInfo[] GetDelegates(object owningObject)
{
List delegates = new List();
FieldInfo[] allPotentialEvents = owningObject.GetType()
.GetFields(PrivatePublicStaticInstance);
foreach (FieldInfo privateFieldInfo in allPotentialEvents)
{
Delegate eventFromOwningObject = privateFieldInfo.GetValue(owningObject)
as Delegate;
if (eventFromOwningObject != null)
{
delegates.Add(new DelegateInfo(eventFromOwningObject, privateFieldInfo,
owningObject));
}
}
return delegates.ToArray();
}
private class DelegateInfo
{
private readonly Delegate delegateInformation;
public Delegate DelegateInformation
{
get { return delegateInformation; }
}
private readonly FieldInfo fieldInfo;
private readonly object owningObject;
public DelegateInfo(Delegate delegateInformation, FieldInfo fieldInfo,
object owningObject)
{
this.delegateInformation = delegateInformation;
this.fieldInfo = fieldInfo;
this.owningObject = owningObject;
}
public Delegate[] GetInvocationList()
{
return delegateInformation.GetInvocationList();
}
public EventInfo GetEventInfo()
{
return owningObject.GetType().GetEvent(fieldInfo.Name,
PrivatePublicStaticInstance);
}
}
}
} using System;using System.Collections.Generic;using System.Globalization;using System.Reflection;namespace TOG.ProductionOutput.Services{publicsealedclass ReflectUtil {privatestatic BindingFlags PrivatePublicStaticInstance = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static;publicdelegatebool MatchesOnDelegate(Delegate subject);publicstaticvoid RemoveEventHandlersFrom( MatchesOnDelegate matchesOnDelegate, paramsobject[] objectsWithEvents) {foreach (object owningObject in objectsWithEvents) {foreach (DelegateInfo eventFromOwningObject in GetDelegates(owningObject)) {foreach (Delegate subscriber in eventFromOwningObject.GetInvocationList()) {if (matchesOnDelegate(subscriber)) { EventInfo theEvent = eventFromOwningObject.GetEventInfo();if(theEvent != null) RemoveSubscriberEvenIfItsPrivate(theEvent, owningObject, subscriber); } } } } }// You can use eventInfo.RemoveEventHandler(owningObject, subscriber) // unless it's a private delegateprivatestaticvoid RemoveSubscriberEvenIfItsPrivate( EventInfo eventInfo, object owningObject, Delegate subscriber) { MethodInfo privateRemoveMethod = eventInfo.GetRemoveMethod(true); privateRemoveMethod.Invoke(owningObject, PrivatePublicStaticInstance, null, newobject[] { subscriber }, CultureInfo.CurrentCulture); }privatestatic DelegateInfo[] GetDelegates(object owningObject) { List delegates = new List(); FieldInfo[] allPotentialEvents = owningObject.GetType() .GetFields(PrivatePublicStaticInstance);foreach (FieldInfo privateFieldInfo in allPotentialEvents) { Delegate eventFromOwningObject = privateFieldInfo.GetValue(owningObject) as Delegate;if (eventFromOwningObject != null) { delegates.Add(new DelegateInfo(eventFromOwningObject, privateFieldInfo, owningObject)); } }return delegates.ToArray(); }privateclass DelegateInfo {privatereadonly Delegate delegateInformation;public Delegate DelegateInformation {get { return delegateInformation; } } privatereadonly FieldInfo fieldInfo;privatereadonlyobject owningObject;public DelegateInfo(Delegate delegateInformation, FieldInfo fieldInfo, object owningObject) {this.delegateInformation = delegateInformation;this.fieldInfo = fieldInfo;this.owningObject = owningObject; }public Delegate[] GetInvocationList() {return delegateInformation.GetInvocationList(); }public EventInfo GetEventInfo() {return owningObject.GetType().GetEvent(fieldInfo.Name, PrivatePublicStaticInstance); } } }}
另外有需要云服务器可以了解下创新互联scvps.cn,海内外云服务器15元起步,三天无理由+7*72小时售后在线,公司持有idc许可证,提供“云服务器、裸金属服务器、高防服务器、香港服务器、美国服务器、虚拟主机、免备案服务器”等云主机租用服务以及企业上云的综合解决方案,具有“安全稳定、简单易用、服务可用性高、性价比高”等特点与优势,专为企业上云打造定制,能够满足用户丰富、多元化的应用场景需求。
当前文章:临时解决方案-RDLC报表内存泄露问题-创新互联
网址分享:http://jxjierui.cn/article/hihig.html


咨询
建站咨询

