当 .NET 4 应用程序中的 AccessViolationException 违背 MSDN 记录的行为时该怎么办?
What to do when AccessViolationException in .NET 4 app defies MSDN documented behavior?
我有一个作为 Windows 服务编写的 .NET 4 应用程序,它包括非托管组件,并且正在向我的 log4net 日志打印 AccessViolationException,但没有使应用程序崩溃。关于这个问题的一切听起来都是不可能的。以下是我认为正在发生的事情,我想知道我作为开发人员的下一步应该是什么?在与生产具有相同数据馈送的并行回放环境中不会发生此问题。
- 内存损坏问题表现为 AccessViolationException,堆栈跟踪归咎于 SSIS 运行时“DTS”。
- DataLoaderWinSvc.exe 是 .NET 4 应用程序,registered/running 作为 Windows 本地服务。
- Microsoft 更改了 .NET 4 中的 CLR,以便 AccessViolationException 导致应用程序默认崩溃。
但是,- DataLoaderWinSvc.exe 不会崩溃。这让我开始调查这怎么可能?
- 有一种方法可以让开发人员在 .NET 4 中处理异常,但是 DataLoaderWinSvc.exe 代码没有使用 [HandleProcessCorruptedStateExceptions] 技术。 https://msdn.microsoft.com/en-us/library/system.accessviolationexception(v=vs.110).aspx#Anchor_6:
Starting with the .NET Framework 4, AccessViolationException
exceptions thrown by the common language runtime are not handled by
the catch statement in a structured exception handler if the exception
occurs outside of the memory reserved by the common language runtime.
To handle such an AccessViolationException
exception, you should apply
the HandleProcessCorruptedStateExceptionsAttribute
attribute to the
method in which the exception is thrown.
- 根据 DataLoaderWinSvc.txt log4net 日志,很明显 DataLoaderWinSvc.exe 能够处理此异常,尽管 运行 作为 .NET Framework 4 应用程序,否则我们不会有任何日志记录语句,只会崩溃。然而,根本没有崩溃。
- 通过使用进程 Explorer.exe,我检查了部署的代码版本是否未使用 .NET 4 Framework。 - 这就是我开始看到可疑之处的地方!事实证明,SSIS 运行时 DLL d:\Program Files (x86)\Microsoft SQL Server0\SDK\Assemblies\Microsoft.SQLServer.ManagedDTS.dll 是一个 .NET 2.0 程序集。
- 看起来,基于进程 Explorer.exe,虽然我调用的 Microsoft.SQLServer.ManagedDTS.dll 是一个 .NET 4 程序集,它实际上将工作委托给了 CLR v2.0.50727使用原生图像组装(ngen's assembly)。
- 如果 AccessViolationException 发生在 CLR v2.0.50727 程序集中,那么这可能就是我们能够捕获这个假定无法捕获的异常的方式。
Here is the anonymized stack trace:
2016-08-16 20:58:38,207 ERROR: Orchestration: Initialization
System.AccessViolationException: Attempted to read or write protected
memory. This is often an indication that other memory is corrupt.
at Microsoft.SqlServer.Dts.Runtime.Package.Execute() at
DataLoader.Orchestration.DoWork() in
D:\SourceCode\DataLoader\Mainline\Core\Orchestration.cs:line 198
System.AccessViolationException: Attempted to read or write protected
memory. This is often an indication that other memory is corrupt.
at Microsoft.SqlServer.Dts.Runtime.Package.Execute() at
DataLoader.Orchestration.DoWork() in
D:\SourceCode\DataLoader\Mainline\Core\Orchestration.cs:line 198
下面是一段大致的代码片段:
public class Orchestration
{
private Application m_DTSApplication;
private Package m_DTSPackage;
public IntradayOrchestration()
{
InitializeDTSPackage();
}
private void InitiatlizeDTSPackage()
{
m_DTSPackageLocation = ConfigurationManager.AppSettings["DTSPackageLocation"].ToString();
m_DTSPackageConfigLocation = ConfigurationManager.AppSettings["DTSPackageConfigLocation"].ToString();
m_DTSApplication = new Application();
m_DTSApplication.PackagePassword = ConfigurationManager.AppSettings["DTSPackagePassword"];
m_DTSPackage = m_DTSApplication.LoadPackage(m_DTSPackageLocation, null);
m_DTSPackage.ImportConfigurationFile(m_DTSPackageConfigLocation);
}
private void DoWork()
{
try
{
Stopwatch sw = Stopwatch.StartNew();
while (true)
{
sw.Start();
DTSExecResult result = m_DTSPackage.Execute();
sw.Stop();
Logger.ReportInfo(DateTime.Now.ToString() + "Time taken by dts package= " + sw.ElapsedMilliseconds.ToString());
// Check retuns status and check for errors on Job execution
if (result != DTSExecResult.Success || m_DTSPackage.Errors != null)
{
if (m_DTSPackage.Errors != null)
{
foreach (DtsError error in m_DTSPackage.Errors)
{
string errorMesg = string.Format("Orchestration: SSIS Package [{0}] Error: Code[{1}] Source[{2}] Component[{3}] Description[{4}]", m_DTSPackage.Name, error.ErrorCode, error.Source, error.SubComponent, error.Description);
Logger.ReportError(errorMesg, new ApplicationException(errorMesg));
}
}
else
{
string errorMesg = string.Format("Orchestration: SSIS Package [{0}] did not complete successfully. Return status [{1}]", m_DTSPackage.Name, result.ToString());
Logger.ReportError(errorMesg, new ApplicationException(errorMesg));
}
}
else
{
Logger.ReportInfo(
string.Format("Orchestration: SSIS Package [{0}] completed successfully", m_DTSPackage.Name));
}
}
catch (Exception ex)
{
Logger.ReportError(
string.Format("Orchestration: SSIS Package [{0}] completed successfully", m_DTSPackage.Name));
}
}
}
根本原因实际上是 "air space" 由于从 .NET 4 到 .NET 2 的跨运行时环境。
此外,事实证明 SQL Server 2008 R2 SSIS 运行时可能在 SQL 日志记录子组件中存在双重空闲内存错误。
我有一个作为 Windows 服务编写的 .NET 4 应用程序,它包括非托管组件,并且正在向我的 log4net 日志打印 AccessViolationException,但没有使应用程序崩溃。关于这个问题的一切听起来都是不可能的。以下是我认为正在发生的事情,我想知道我作为开发人员的下一步应该是什么?在与生产具有相同数据馈送的并行回放环境中不会发生此问题。
- 内存损坏问题表现为 AccessViolationException,堆栈跟踪归咎于 SSIS 运行时“DTS”。
- DataLoaderWinSvc.exe 是 .NET 4 应用程序,registered/running 作为 Windows 本地服务。
- Microsoft 更改了 .NET 4 中的 CLR,以便 AccessViolationException 导致应用程序默认崩溃。 但是,
- DataLoaderWinSvc.exe 不会崩溃。这让我开始调查这怎么可能?
- 有一种方法可以让开发人员在 .NET 4 中处理异常,但是 DataLoaderWinSvc.exe 代码没有使用 [HandleProcessCorruptedStateExceptions] 技术。 https://msdn.microsoft.com/en-us/library/system.accessviolationexception(v=vs.110).aspx#Anchor_6:
Starting with the .NET Framework 4,
AccessViolationException
exceptions thrown by the common language runtime are not handled by the catch statement in a structured exception handler if the exception occurs outside of the memory reserved by the common language runtime. To handle such anAccessViolationException
exception, you should apply theHandleProcessCorruptedStateExceptionsAttribute
attribute to the method in which the exception is thrown.
- 根据 DataLoaderWinSvc.txt log4net 日志,很明显 DataLoaderWinSvc.exe 能够处理此异常,尽管 运行 作为 .NET Framework 4 应用程序,否则我们不会有任何日志记录语句,只会崩溃。然而,根本没有崩溃。
- 通过使用进程 Explorer.exe,我检查了部署的代码版本是否未使用 .NET 4 Framework。 - 这就是我开始看到可疑之处的地方!事实证明,SSIS 运行时 DLL d:\Program Files (x86)\Microsoft SQL Server0\SDK\Assemblies\Microsoft.SQLServer.ManagedDTS.dll 是一个 .NET 2.0 程序集。
- 看起来,基于进程 Explorer.exe,虽然我调用的 Microsoft.SQLServer.ManagedDTS.dll 是一个 .NET 4 程序集,它实际上将工作委托给了 CLR v2.0.50727使用原生图像组装(ngen's assembly)。
- 如果 AccessViolationException 发生在 CLR v2.0.50727 程序集中,那么这可能就是我们能够捕获这个假定无法捕获的异常的方式。
Here is the anonymized stack trace:
2016-08-16 20:58:38,207 ERROR: Orchestration: Initialization System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
at Microsoft.SqlServer.Dts.Runtime.Package.Execute() at DataLoader.Orchestration.DoWork() in D:\SourceCode\DataLoader\Mainline\Core\Orchestration.cs:line 198 System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
at Microsoft.SqlServer.Dts.Runtime.Package.Execute() at DataLoader.Orchestration.DoWork() in D:\SourceCode\DataLoader\Mainline\Core\Orchestration.cs:line 198
下面是一段大致的代码片段:
public class Orchestration
{
private Application m_DTSApplication;
private Package m_DTSPackage;
public IntradayOrchestration()
{
InitializeDTSPackage();
}
private void InitiatlizeDTSPackage()
{
m_DTSPackageLocation = ConfigurationManager.AppSettings["DTSPackageLocation"].ToString();
m_DTSPackageConfigLocation = ConfigurationManager.AppSettings["DTSPackageConfigLocation"].ToString();
m_DTSApplication = new Application();
m_DTSApplication.PackagePassword = ConfigurationManager.AppSettings["DTSPackagePassword"];
m_DTSPackage = m_DTSApplication.LoadPackage(m_DTSPackageLocation, null);
m_DTSPackage.ImportConfigurationFile(m_DTSPackageConfigLocation);
}
private void DoWork()
{
try
{
Stopwatch sw = Stopwatch.StartNew();
while (true)
{
sw.Start();
DTSExecResult result = m_DTSPackage.Execute();
sw.Stop();
Logger.ReportInfo(DateTime.Now.ToString() + "Time taken by dts package= " + sw.ElapsedMilliseconds.ToString());
// Check retuns status and check for errors on Job execution
if (result != DTSExecResult.Success || m_DTSPackage.Errors != null)
{
if (m_DTSPackage.Errors != null)
{
foreach (DtsError error in m_DTSPackage.Errors)
{
string errorMesg = string.Format("Orchestration: SSIS Package [{0}] Error: Code[{1}] Source[{2}] Component[{3}] Description[{4}]", m_DTSPackage.Name, error.ErrorCode, error.Source, error.SubComponent, error.Description);
Logger.ReportError(errorMesg, new ApplicationException(errorMesg));
}
}
else
{
string errorMesg = string.Format("Orchestration: SSIS Package [{0}] did not complete successfully. Return status [{1}]", m_DTSPackage.Name, result.ToString());
Logger.ReportError(errorMesg, new ApplicationException(errorMesg));
}
}
else
{
Logger.ReportInfo(
string.Format("Orchestration: SSIS Package [{0}] completed successfully", m_DTSPackage.Name));
}
}
catch (Exception ex)
{
Logger.ReportError(
string.Format("Orchestration: SSIS Package [{0}] completed successfully", m_DTSPackage.Name));
}
}
}
根本原因实际上是 "air space" 由于从 .NET 4 到 .NET 2 的跨运行时环境。
此外,事实证明 SQL Server 2008 R2 SSIS 运行时可能在 SQL 日志记录子组件中存在双重空闲内存错误。