起因:最近公司的一个web产品遇到了内存溢出,于是开始着手调查。 调查:首先当务之急是找到那个或那些API导致MemoryLeak,这个应该不难,根据监控分析,在内存上升时间段内有哪些API被访问,再就是根据开发人员提供信息,评估可能内存溢出的API。 案例分析:划定API范围后,用压力测试来重现问题,进一步定位具体是哪个方法,哪个技术点出问题,这些应该也不难,难的是知道什么地方有问题,但不知道怎么解决。 下面是经过精简后,分离出的有问题的代码。 项目文件csproj:ProjectSdkMicrosoft。NET。SdkPropertyGroupOutputTypeExeOutputTypeTargetFrameworknet6。0TargetFrameworkImplicitUsingsenableImplicitUsingsNullableenableNullableServerGarbageCollectionfalseServerGarbageCollectionConcurrentGarbageCollectiontrueConcurrentGarbageCollectionPropertyGroupItemGroupPackageReferenceIncludeClosedXMLVersion0。96。0PackageReferenceIncludeQRCoderVersion1。4。2ItemGroupProject 具体代码program。cs:usingClosedXML。Excel;usingDocumentFormat。OpenXml。Spreadsheet;usingQRCoder;usingSystem。Diagnostics;usingSystem。Drawing;usingSystem。Drawing。Imaging;usingSystem。Reflection。Emit;usingSystem。Runtime。InteropServices;usingColorSystem。Drawing。Color;Console。WriteLine(回车开始);Console。ReadLine();varqrGeneratornewQRCodeGenerator();while(true){usingvarworkBooknewXLWorkbook();varwsworkBook。Worksheets。Add(TestSheet);for(vari1;i200;i){usingvarqrCodeDataqrGenerator。CreateQrCode(34;{i}{DateTime。Now。ToString(yyMMddHHmmssfffffff)}01PAe8np5m7pVULUiuxwwZTWQ9KZ8JUgQyWiyUrsiHqi4FKrCzhRAcddCkkJKDgVEkpmqD7kYJz5GTpe4oHvJdJDnNMMCTwbV19G,QRCodeGenerator。ECCLevel。L);usingvarqrCodenewQRCode(qrCodeData);usingvarqrCodeImageqrCode。GetGraphic(20,Color。Black,Color。White,false);usingvarimgStreamnewMemoryStream();qrCodeImage。Save(imgStream,ImageFormat。Png);usingvarimagews。AddPicture(imgStream)。MoveTo(ws。Cell(i,1),newPoint(50,10));image。Width60;image。Height60;}workBook。SaveAs(34;appimages{i}{DateTime。Now。ToString(yyyyMMddHHmmss)}。xlsx);Console。WriteLine(34;完了:{DateTime。Now});Console。ReadLine();} 很有趣的是,这块代码在docker里运行时会有memoryleak,但在windows上是没有问题的。 上面代码主要有两个功能,生成二维码,然后保存在Excel里,最后保存本地。具体表现是,每生成一个Excel,内存就会增加,不释放,直到容器重启。 其实找性能问题是一个排查的过程,首先要一点一点把认为有问题的代码修正,再次进行测试,看是否有改善,比如主动去释放一些对象,使用using,或者直接用GC。Collect()来测试。经过一轮后发现没有改善内存只涨不降的现象。于是就进行功能分享,把QR生成换成静态图片,查看Excel生成部分是否有问题,这样很快就定位到了QR生成有问题,但QR生成能有什么问题呢?并且这个功能一直在用,从。net5用到。net6,都没有变更过这里的代码。那只能查看是QR生成有无问题了。 下面是经过一番查找后的结果,果然ORCode类对。net6。0不能很好的支持,只能换掉它了,替代的方式是用PngByteQRCode。 https:github。comcodebudeQRCoderwikiAdvancedusageQRCoderenderers2overviewofthedifferentrenderers 修改后的代码是:usingClosedXML。Excel;usingDocumentFormat。OpenXml。Spreadsheet;usingQRCoder;usingSystem。Diagnostics;usingSystem。Drawing;usingSystem。Drawing。Imaging;usingSystem。Reflection。Emit;usingSystem。Runtime。InteropServices;usingColorSystem。Drawing。Color;Console。WriteLine(回车开始);Console。ReadLine();varqrGeneratornewQRCodeGenerator();while(true){usingvarworkBooknewXLWorkbook();varwsworkBook。Worksheets。Add(TestSheet);for(vari1;i200;i){byte〔〕qrCodeAsBitmapByteArrPngByteQRCodeHelper。GetQRCode(DateTime。Now。ToString(yyMMddHHmmssfffffff)01PAe8np5m7pVULUiuxwwZTWQ9KZ8JUgQyWiyUrsiHqi4FKrCzhRAcddCkkJKDgVEkpmqD7kYJz5GTpe4oHvJdJDnNMMCTwbV19G,QRCodeGenerator。ECCLevel。Q,20,false);varimgStreamnewMemoryStream(qrCodeAsBitmapByteArr);imgStream。Seek(0,SeekOrigin。Begin);usingvarimagews。AddPicture(imgStream)。MoveTo(ws。Cell(i,1),newPoint(50,10));image。Width60;image。Height60;}workBook。SaveAs(34;C:UsersaxzxsPictures{DateTime。Now。ToString(yyyyMMddHHmmss)}。xlsx);Console。WriteLine(34;完了:{DateTime。Now});Console。ReadLine();} 其实单看上面过程,很简单,其实测试过程中的一些指标表象,以及个人对技术点的认知,都会让性能分析起来要走一些弯路。比如上面功能,其实我们花了很多时间在Excel的生成找问题,甚至换了Excel生成组件来进行测试,经过多轮出修改方案,修改代码,压力测试,结果分析,才最终定义QR码,又经过多轮对QR码生成的方案优化,最后才找到是因为QR不支持导致的结果。 性能分析就是找疑难杂症,是个痛苦过程,同时,解决掉,又是一个很喜悦的收获。