做网站推广销售怎么样,天津如何做seo优化服务,网站网页设计专业公司,网站手机端生成缘起 最近项目中遇到一个诡异的问题#xff0c;程序在升级到.net4.6.1后#xff0c;执行某个功能时会崩溃#xff0c;提示访问只读内存区。大概规律如下:debug版不崩溃#xff0c;release版稳定崩溃。只有x64位的程序崩溃#xff0c;32位及anycpu编译出来的程序运行不会崩… 缘起 最近项目中遇到一个诡异的问题程序在升级到.net4.6.1后执行某个功能时会崩溃提示访问只读内存区。大概规律如下:debug版不崩溃release版稳定崩溃。只有x64位的程序崩溃32位及anycpu编译出来的程序运行不会崩溃。出问题的代码范围很小按钮点击事件代码不多。根据以上信息各位小伙伴有什么思路吗排查 由于release版可以稳定重现而且范围不大故通过二分法每次注释掉一半代码看看是否崩溃如果崩溃接着注释掉一半代码如果不崩溃说明崩溃跟注释掉的那段代码有关...很快定位到了导致问题的代码。最后发现并不是由于升级.net版本导致的而是程序本身的问题代码中通过P/Invoke调用了原生 API GlobalMemoryStatus()。在定义MemoryStatus结构体的时候强制按4字节定义了每一个字段。而在x64下MemoryStatus结构体中的成员有些不是4字节大小而是8字节大小这样传递给GlobalMemoryStatus()的MemoryStatus参数32字节比GlobalMemoryStatus()预期的56字节小导致GlobalMemoryStatus写了不该写的内存????????????重现 我把有问题的代码独立出来了完整的测试代码如下(请编译x64版本)using System;using System.Runtime.InteropServices;namespace ConsoleApplication1{ class Program { [StructLayout(LayoutKind.Sequential)] public struct MemoryStatus { [MarshalAs(UnmanagedType.U4)] public uint dwLength; [MarshalAs(UnmanagedType.U4)] public uint dwMemoryLoad; [MarshalAs(UnmanagedType.U4)] public uint dwTotalPhys; [MarshalAs(UnmanagedType.U4)] public uint dwAvailPhys; [MarshalAs(UnmanagedType.U4)] public uint dwTotalPageFile; [MarshalAs(UnmanagedType.U4)] public uint dwAvailPageFile; [MarshalAs(UnmanagedType.U4)] public uint dwTotalVirtual; [MarshalAs(UnmanagedType.U4)] public uint dwAvailVirtual; } [DllImport(kernel32.dll)] public static extern void GlobalMemoryStatus(ref MemoryStatus memoryStatus); class CMyClass { public int n1 0; } struct CMyStruct { public CMyClass data; } static void Main(string[] args) { CMyStruct myObj new CMyStruct(); myObj.data new CMyClass(); MemoryStatus memoryStatus new MemoryStatus(); // this line will corrupt the stack if we run in x64. // because memoryStatus is defined on the stack. GlobalMemoryStatus(ref memoryStatus); // myObj.data is corrupted System.Console.WriteLine({0}, myObj.data); } }}修复 只需要定义MemoryStatus的时候注意字段的大小即可。正确的MemoryStatus定义如下public struct MemoryStatus{ [MarshalAs(UnmanagedType.U4)] public uint dwLength; [MarshalAs(UnmanagedType.U4)] public uint dwMemoryLoad; // 以下字段 4 bytes on 32-bit Windows, 8 bytes on 64-bit Windows. [MarshalAs(UnmanagedType.SysUInt)] public IntPtr dwTotalPhys; [MarshalAs(UnmanagedType.SysUInt)] public IntPtr dwAvailPhys; [MarshalAs(UnmanagedType.SysUInt)] public IntPtr dwTotalPageFile; [MarshalAs(UnmanagedType.SysUInt)] public IntPtr dwAvailPageFile; [MarshalAs(UnmanagedType.SysUInt)] public IntPtr dwTotalVirtual; [MarshalAs(UnmanagedType.SysUInt)] public IntPtr dwAvailVirtual;}思考 为什么debug版不崩溃而release版会崩溃我在测试机器上调查的原因是debug版本运行的时候关键内存恰巧没被破坏太“幸运”或者太不幸了而在release版本中暴露了问题。可能在其它机器上debug版本也会崩溃或者发生其它诡异的问题。说明测试代码与项目中的实际代码不一样有可能现象不一样但问题的本质是一样的。为什么运行Any CPU编译出来的程序不崩溃当Platform target是Any CPU的时候在工程属性Build下的Prefer 32-bit的选项默认是勾选的编译的程序会作为 32 位进程运行所以不会崩溃。如果取消勾选则编译出来的程序会作为 64 位应用程序运行会崩溃。build settings关于Platform target的作用具体参考《CLR via C#》下图是从《CLR via C#》中文版第 4 版上截取的。/platform option 截自《CLR via C#》总结 .net程序中令人头疼的内存破坏问题很难出现了这极大的提高了程序的稳定性。如果出现堆破坏很有可能跟P/Invoke或者unsafe代码相关可以重点排查相关代码。启用托管调试助手(Managed Debugging Assistants, 下文简称MDAs) 有时候会对调试问题有极大的帮助虽然我这次调试没有借助MDAs但我第一个想到的就是MDAs。关于MDAs的介绍请参考参考资料第一条。参考资料 Managed Debugging Assistants[1]GlobalMemoryStatus[2]《CLR via C#》[3]References[1] Managed Debugging Assistants:https://docs.microsoft.com/en-us/dotnet/framework/debug-trace-profile/diagnosing-errors-with-managed-debugging-assistants[2] GlobalMemoryStatus:https://docs.microsoft.com/zh-cn/windows/win32/api/winbase/nf-winbase-globalmemorystatus?redirectedfromMSDN[3] 《CLR via C#》: https://book.douban.com/subject/4924165/写留言