热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

AWSS3.NETClient高内存使用率

将AWSS3.NET客户端LOH分配减少98%内容问题发现为什么会出问题呢?推出最佳魔幻数字—81,920空闲的手还有一件事TLDR-给我好东西脚注问题

将AWS S3 .NET客户端LOH分配减少98%

内容

  • 问题发现
  • 为什么会出问题呢?
  • 推出最佳魔幻数字— 81,920
  • 空闲的手
  • 还有一件事
  • TLDR-给我好东西
  • 脚注

问题发现

我们在Codeweavers所做的一件事就是帮助人们找到他们的下一辆车。 通常,这涉及到客户看到他们要购买的汽车-我的意思是,您会在不看外观的情况下购买汽车吗? 持有这个责任该应用程序是为淫秽金额分配,时间GC上花费的,而像饼干怪兽吃得好...饼干一般吃RAM 最坏的罪犯。

我们不时地希望从生产环境中获取此应用程序的内存转储。 我们已经完成了足够的次数,以使我们执行的最常见诊断步骤自动化,并将其捆绑到一个称为ADA(自动转储分析)的小工具中。 如果你有兴趣,你可以找到的工具, 在这里 ,所有谈到本文中的代码在这里 。

我们运行的分析器之一是转储在大对象堆(LOH)上找到的所有byte []数组。 针对我们的8 GB内存转储运行该分析器之后,我们发现了数百个byte []数组,长度为131,096或131,186。 好吧,这很奇怪。 在Notepad ++中打开某些文件只会给我们带来很多随机字符。

我把科学方法丢到窗外一秒钟,我决定将所有转储的byte []数组重命名为* .jpg-嘿,现在有些文件现在显示缩略图! 经过仔细检查,大约50%的文件是图像。 其余50%都无法完全打开为图像。 在Notepad ++中打开少​​数非图像文件显示,它们在文件开头都有一行类似于此的行:-

0;chunk-signature=48ebf1394fcc452801d4ccebf0598177c7b31876e3fbcb7f6156213f931b261d

好的,这开始变得更有意义了。 长度为131,096的byte []数组是纯图像。 不是图像的byte []数组的长度为131,186,并在其余内容之前具有块签名行。 我猜想签名是内容的SHA256哈希。

在继续进行之前,值得确定该应用程序在图像处理方面的繁忙程度。 我们所有的图像处理都使用AWS SNS和SQS在整个服务器场中分布。 使用CloudWatch指标,我们可以轻松看到:

好了,所以相当繁忙。 值得注意的是,在进行任何以性能为中心的工作之前,请务必确定执行代码的频率和当前成本。 如果代码路径的成本很高(例如,花费二十秒钟),但是每天只命中一次,则不值得研究。 但是,如果相同的代码路径被击中很多(例如一天一百万次),那么绝对值得研究。

此时,我想到了两个罪魁祸首。 我们已经建立了有问题的应用程序来做很多图像处理。 但是有一些运动的部分和两种启动图像处理的方式:

  1. 图片被推送给我们
  2. 我们从SFTP提取图像

之后,我们转换图像,然后将其上传到AWS S3 。 在此阶段,我倾向于使用SFTP,因为它可能需要验证从服务器接收到的每个块。 但是,随着我的预感,我开始追逐野鹅,于是我无视我的直觉,便将大块签名插入了Google并粉碎了输入。 谷歌指出AWS S3是罪魁祸首 。 但这仅仅是理论,我们需要证明这一点。

如果我们上传相同的图像十次并使用dotTrace来查看LOH,则会看到一个有趣的模式:

每次我们在AWS S3 .NET客户端上调用PutObject时,看起来固定成本为0.3 MB。 这是一个问题,因为这意味着每次使用PutObject时,每次上载都要付出0.3 MB的高昂费用。 只想确认一下; 如果将上传次数从十次增加到一百次会发生什么?

是的,我们可以肯定地说,每次调用PutObject都会产生0.3 MB的昂贵分配。 更进一步,并使用ProcDump转储该过程:-

procdump64.exe -ma -64 AWS-S3.exe

通过ADA运行转储文件,我们看到有两组byte []数组具有完全相同的特性; 50%的长度为131,096,其他50%的长度为131,186。 重命名时,文件的一半是图像,文件的一半具有块签名起始行。 至此,我们可以确定,AWS S3 .NET客户端正在将byte []数组直接分配到LOH上。 那个问题。

为什么会出问题呢?

LOH是一个已收集但从未压缩的内存区域,尽管从现在开始可以进行.NET v4.5.1 压缩 ,但警告词对LOH的压缩非常昂贵。 每兆字节大约2.3毫秒 。 一条很好的经验法则是,短暂存在的物体不能进入LOH。

等于或大于85,000字节的对象直接进入LOH。 LOH的操作与其他内存区域大不相同。 其他内存区域会定期收集和压缩,这意味着您可以在垃圾收集器运行后将新对象添加到末尾。 而LOH会尝试将新分配的对象放入废弃对象遗留的剩余空间中。 如果新分配的对象的大小与可用空间完全相同或较小,则此方法很好用。 如果找不到空间,则LOH必须增长以容纳该对象。

它有助于像书架一样思考它; 在内存的其他区域中,不再使用的书籍被简单地扔掉,剩余的书籍被推到一起,所有新书都放在书架的尽头。

在不可能的LOH内,将丢弃书籍(对象),并记录该空间中曾经使用的页数(字节),并在下次将书分配到该书架时(LOH)它试图找到一个可以容纳那么多页面(字节)的空白空间。 如果书架无法容纳新分配的书(对象),则必须扩展书架以容纳新书(对象)。

垃圾收集器将从LOH收集死对象,与此同时,将新对象分配给LOH。 这可能导致长时间运行的应用程序出现生命周期的情况,其中LOH大小已增加到几GB(因为新对象无法容纳到现有的空白空间中),但实际上仅包含几个活动对象。 这称为LOH碎片。 在这种情况下,我们感到非常幸运,因为将其放入LOH的byte []数组有两种大小。 131,186和131,096。 这意味着,无论哪种大小的旧对象都死掉并被收集起来,新分配的对象恰好适合将其放入空白空间的大小。

好吧,回到有趣的东西。

推出最佳魔幻数字— 81,920

多亏了dotTrace,我们才能够准确地确定导致LOH碎片的原因。 它还向我们显示,每次调用PutObject的固定成本0.3 MB发生在ChunkedUploadWrapperStream的构造函数内部:

快速访问该文件在AWS-SDK-网库。 显示创建的两个byte []数组的长度至少为131,072:-

这就是为什么将这些byte []数组直接分配给LOH的原因,它们高于LOH阈值(85,000字节)。 此时,有几种可能的解决方案:

  1. 使用System.Buffers从byte []数组池中租用两个byte []数组
  2. 使用Microsoft.IO.RecycableMemoryStream并使用Stream的池直接对传入流进行操作
  3. 公开DefaultChunkSize,以便API的使用者可以自行设置
  4. 将DefaultChunkSize降低到低于LOH阈值的数字(85,000字节)

第一个和第二个解决方案可能是获得最大胜利的解决方案,但这将需要大量的请求,并引入了库维护者可能不希望的依赖项²。 第三种解决方案意味着图书馆的使用者必须了解该问题并将其设置为合理的数目,以避免分配LOH。 不,似乎第四种解决方案最有可能被接受,破坏现有功能的可能性也最小。

我们所需要的是一个低于85,000的数字,通常84,000之类的数字非常合适。 但是,在发现此问题的几周前,当我偶然发现这个宝石时,我正在参考参考源 (正在研究另一个问题):

Windows内存页的大小为4,096字节 ,因此选择低于LOH阈值(85,000字节)的倍数是完全有意义的。 是时候分叉,分支, 创建问题并提出拉取请求了 。

幸运的是,我们可以在本地进行更改³,看看有什么好处。 通过PutObject上传同一图像的一百次统计:-

空闲的手

在等待对我的请求请求进行审查的同时,我决定查阅AWS S3文档,偶然发现了预签名URL的概念。 听起来很有趣! 创建上传器的V2:-

上载相同文件一百次时,我们看到它具有以下统计信息:-

那真是太棒了,而我们实际上要做的就是阅读文档! 好吧,这是不对的,您的好处是可以阅读包含所有内容的摘要文章。 您在此处看到的工作是在一周的时间内完成的,介于两次客户工作之间。

使用GetPreSignedURL的一个小缺点是,如果修改了GetPreSignedUrlRequest并且未相应地修改WebRequest,则AWS将返回HTTP 403 Forbidden (例如,删除WebRequest上的XAmzAclHeader)。 这是因为客户端哈希和服务器哈希不再匹配。

还有一件事

多亏了我的上一篇文章,我才知道什么是书呆子狙击 -我对自己做了很多事情。 在此阶段,我感到对其他东西可以省掉有点头晕,我一直在看着LOH上剩余的0.4 MB。 同样,dotTrace将我们指向代码路径的方向,导致向LOH分配了0.4 MB:-

耶克斯, 那看起来很严肃 。 安静地退后一步,尝试不同的策略; 我们知道一个预签名的URL看起来像这样:

https://##bucket_name##.s3.##region_name##.amazonaws.com/##path##/##file_name##?X-Amz-Expires=300&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=##access_key##/20180613/##region_name##/s3/aws4_request&X-Amz-Date=20180613T233349Z&X-Amz-SignedHeaders=host;x-amz-acl&X-Amz-Signature=6bbcb0f802ad86022674e827d574b7a34a00ba76cd1411016c3581ba27fa5450

我们应该能够自行生成该URL,因为AWS非常友好地发布了其签名过程 。 在这一点上,我承认我已经准备好接受失败了,只是将0.4 MB留给了LOH。 我真的不喜欢的代码里姆斯我正要写可能消除剩余的0.4 MB将是值得的。

直到我发现我想要的例子为止。 我的工作量大大减少了; V3诞生了:

V3只是一个实验,旨在了解可能的情况,因为所获得的收益和维护的代码量不大,这并不是我们在生产代码中实际使用的东西。 预签名URL的发现是这里的主要优势:-

同时,我的拉取请求已合并并发布在AWSSDK.Core的3.3.21.19版本中。 时间轴快速概述:-

  1. 2018–03–07 —在aws-sdk-net存储库上创建的问题
  2. 2018-03-13 —发送了拉取请求
  3. 2018-03-29 —合并请求请求
  4. 2018–03–29 —新版本的AWSSDK.Core已发布到NuGet

我喜欢开源。

TLDR-给我好东西

低于3.3.21.19的AWSSDK.Core版本导致在AWS S3 .NET客户端上每次PutObject调用的固定成本为0.3 MB。 此问题已在3.3.21.19及更高版本中得到纠正。 对于特别热的代码路径,值得探索在AWS S3 .NET客户端上使用GetPreSignedURL,因为在我们的上下文和用例中,LOH分配减少了98%。

找到我Twitter , LinkedIn或GitHub 。

脚注

¹另一个原因可能是WinDbg仍然吓到我了。

²话虽如此, 最近的一次对话已经开始利用.NET Core的优点。

³确保不像个人那样构建发布版本-好的,是我

本来在发表 dev.to

From: https://hackernoon.com/its-not-your-code-vol-i-c06fac8784df



推荐阅读
  • 本文介绍了Swing组件的用法,重点讲解了图标接口的定义和创建方法。图标接口用来将图标与各种组件相关联,可以是简单的绘画或使用磁盘上的GIF格式图像。文章详细介绍了图标接口的属性和绘制方法,并给出了一个菱形图标的实现示例。该示例可以配置图标的尺寸、颜色和填充状态。 ... [详细]
  • C语言注释工具及快捷键,删除C语言注释工具的实现思路
    本文介绍了C语言中注释的两种方式以及注释的作用,提供了删除C语言注释的工具实现思路,并分享了C语言中注释的快捷键操作方法。 ... [详细]
  • 图像因存在错误而无法显示 ... [详细]
  • 本文介绍了关系型数据库和NoSQL数据库的概念和特点,列举了主流的关系型数据库和NoSQL数据库,同时描述了它们在新闻、电商抢购信息和微博热点信息等场景中的应用。此外,还提供了MySQL配置文件的相关内容。 ... [详细]
  • ejava,刘聪dejava
    本文目录一览:1、什么是Java?2、java ... [详细]
  • 本文介绍了数据库的存储结构及其重要性,强调了关系数据库范例中将逻辑存储与物理存储分开的必要性。通过逻辑结构和物理结构的分离,可以实现对物理存储的重新组织和数据库的迁移,而应用程序不会察觉到任何更改。文章还展示了Oracle数据库的逻辑结构和物理结构,并介绍了表空间的概念和作用。 ... [详细]
  • 本文介绍了Perl的测试框架Test::Base,它是一个数据驱动的测试框架,可以自动进行单元测试,省去手工编写测试程序的麻烦。与Test::More完全兼容,使用方法简单。以plural函数为例,展示了Test::Base的使用方法。 ... [详细]
  • 本文介绍了在Vue项目中如何结合Element UI解决连续上传多张图片及图片编辑的问题。作者强调了在编码前要明确需求和所需要的结果,并详细描述了自己的代码实现过程。 ... [详细]
  • 本文介绍了Linux Shell中括号和整数扩展的使用方法,包括命令组、命令替换、初始化数组以及算术表达式和逻辑判断的相关内容。括号中的命令将会在新开的子shell中顺序执行,括号中的变量不能被脚本余下的部分使用。命令替换可以用于将命令的标准输出作为另一个命令的输入。括号中的运算符和表达式符合C语言运算规则,可以用在整数扩展中进行算术计算和逻辑判断。 ... [详细]
  • Java中包装类的设计原因以及操作方法
    本文主要介绍了Java中设计包装类的原因以及操作方法。在Java中,除了对象类型,还有八大基本类型,为了将基本类型转换成对象,Java引入了包装类。文章通过介绍包装类的定义和实现,解答了为什么需要包装类的问题,并提供了简单易用的操作方法。通过本文的学习,读者可以更好地理解和应用Java中的包装类。 ... [详细]
  • 在Oracle11g以前版本中的的DataGuard物理备用数据库,可以以只读的方式打开数据库,但此时MediaRecovery利用日志进行数据同步的过 ... [详细]
  • 流数据流和IO流的使用及应用
    本文介绍了流数据流和IO流的基本概念和用法,包括输入流、输出流、字节流、字符流、缓冲区等。同时还介绍了异常处理和常用的流类,如FileReader、FileWriter、FileInputStream、FileOutputStream、OutputStreamWriter、InputStreamReader、BufferedReader、BufferedWriter等。此外,还介绍了系统流和标准流的使用。 ... [详细]
  • 原文转自:http:blog.csdn.netchinasoftosgarticledetails7903045UploadAction.java:packagecr ... [详细]
  • PHP WEB项目文件夹上传下载解决方案
    PHP用超级全局变量数组$_FILES来记录文件上传相关信息的。1.file_uploadsonoff是否允许通过http方式上传文件2.max_execution_time3 ... [详细]
  • 在应用系统开发当中,文件的上传和下载是非常普遍的需求。在基于.NET的CS架构的项目开发当中,有多种方案可以实现文件的上传和下载(httpwebrequest、webclient等),而且多采用异步 ... [详细]
author-avatar
matt
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有