惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Recorded Future
Recorded Future
Apple Machine Learning Research
Apple Machine Learning Research
博客园_首页
S
SegmentFault 最新的问题
博客园 - 司徒正美
Last Week in AI
Last Week in AI
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
云风的 BLOG
云风的 BLOG
雷峰网
雷峰网
博客园 - 叶小钗
The GitHub Blog
The GitHub Blog
MyScale Blog
MyScale Blog
腾讯CDC
博客园 - 聂微东
D
DataBreaches.Net
博客园 - Franky
人人都是产品经理
人人都是产品经理
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
博客园 - 【当耐特】
量子位
宝玉的分享
宝玉的分享
D
Docker
T
Tailwind CSS Blog
IT之家
IT之家
Engineering at Meta
Engineering at Meta
P
Proofpoint News Feed
C
CERT Recently Published Vulnerability Notes
Scott Helme
Scott Helme
Project Zero
Project Zero
Microsoft Azure Blog
Microsoft Azure Blog
AWS News Blog
AWS News Blog
Google DeepMind News
Google DeepMind News
H
Heimdal Security Blog
W
WeLiveSecurity
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
有赞技术团队
有赞技术团队
Simon Willison's Weblog
Simon Willison's Weblog
NISL@THU
NISL@THU
C
Cybersecurity and Infrastructure Security Agency CISA
Google DeepMind News
Google DeepMind News
T
Threatpost
TaoSecurity Blog
TaoSecurity Blog
N
News and Events Feed by Topic
aimingoo的专栏
aimingoo的专栏
Recent Commits to openclaw:main
Recent Commits to openclaw:main
www.infosecurity-magazine.com
www.infosecurity-magazine.com
SecWiki News
SecWiki News
S
Securelist

博客园 - Sumtec

This is it GOODBYE 博客园(附最后一个QUIZ部分不完整答案) 答案of QUIZ:一个网页上面有多少个SilverLight4应用时会发生莫名其妙的崩溃?和什么有关? QUIZ:一个网页上面有多少个SilverLight4应用时会发生莫名其妙的崩溃?和什么有关? QUIZ:一个有8个属性的匿名类大约会占多大的文件大小? QUIZ:mscorlib当中最占文件空间的类是哪一个类? 大家来C#微群吧! 优秀程序员的修炼 做一个有独立脑袋,愿意思考的人 如何不成为傻瓜 世界主要是由傻瓜组成的 很简单的问题 解决问题不是光凭感觉和Google的 在创业团队图什么 支付宝的神奇体验 关注的都来一下,谢谢 拆迁条例据说也要有变化了 5年又5个月了 谁知道C# 5.0都会有些什么?
答案 of QUIZ:mscorlib当中最占文件空间的类是哪一个类?
Sumtec · 2011-08-11 · via 博客园 - Sumtec

答案是:System.Security.Cryptography (.NET FX 4.0,见下图)

 

这个Quiz看过的人不超过160个,说明大家越来越水了。打口水仗各个精神百倍,唾沫星子四处飞。真正来实际的,就全都瞎火了。还记得很久之前某人的一个说.NET咋咋不好的一个系列贴吗,其中有一集说Metadata很大又没有用应该去掉等。当时这个帖子多么火热啊!可惜是否真这样,谁又真的研究过呢?要挑事一定要拿出证据来,批驳别人也一样的。当时我对此兴趣缺缺,只想说这不是什么重要的问题,大家洗洗睡吧,所以也就没多深挖。后来因为有SL4的项目,需要对XAP包瘦身,于是就顺道研究了一下.NET的DLL空间大小都分布在什么样的地方。正因如此,就自己写了一个小工具,可以看到底是什么占空间。

比如拿mscorlib为例吧,整个文件中大约60%的空间是IL代码,剩余的40%是MetaData,这其中的60%(也就是整个文件的约24%)由TableHeap占用。而TableHeap当中,MethodTable占了36%(整个文件的8.64%),ParamTable占了21%(整个文件的5%),CustomAttributeTable占了12%(2.88%),FieldTable占10%(2.4%)……(见下图)

 

等会儿,什么是MetaData?那些个什么Heap又是什么,Table又是什么?图中那个.text什么的又是啥?好,我们从大往小讲,先说说.text。

Windows的可执行文件会根据所承载的内容放在不同的段里面,比如.text就是放代码的段,可读可执行不可写;其实一般的应用有可能还有另一个.data的数据段,不可执行可读写。对于.NET的可执行文件(包括dll)来说,只有.text段,所有你的代码资源什么的通通被打包到这个段里面。好,这部分就解释到此,更细节的就于此无关了。

接下来,我们说说堆。.NET文件的堆包括以下几个:

BlobHeap,用于保存各种大小类型不一的元数据,比如方法的签名信息等;

GuidHeap,用于保存各种程序级别的GUID,一般来讲你可以忽视它;

StringHeap,用于保存程序本身的字符串,例如命名空间名、函数名、类名等等,这个部分是UTF-8的C编码字符串,也就是以0结尾的;

TableHeap,用于保存各种数据长度固定的表,后面再详细解释;

UserStringHeap,用于保存程序中的各种字符串常量,比如:

string s = "你好"; // 这个“你好”就会被保存在这个堆当中。这个堆和StringHeap的区别是,他使用UTF-16来保存的,并且是.NET格式编码,即,前面一个7BitEncoded的表示长度的数值,后面跟着字符串。

现在,我们不清楚的就该剩下TableHeap了,这里我不做详细的解释了,只拿一个MethodTable来说明问题。MethodTable会记录该方法的名称指针(指向StringHeap),方法的签名指针(也就是这个方法有多少个参数,分别是什么类型等等,指向BlobHeap),方法参数的描述指针(比如每一个参数是否和COM的什么描述有关,或者参数名是什么等等,指向ParamTable),方法的其它各种常规属性比如调用制式,以及一个指向Body的指针等。当然了,我们可以想象,还会描述这里面有哪些类的TypeDefTable,这个类里面都有什么属性的PropertyTable,有哪些字段的FieldTable……

如果你在这个的基础之上再去看看IL的格式,就会发现IL里面对函数的调用等,都不是直接给出一个地址,而是一个MetaData中的记录编号(第几个方法)。即便我们不需要反射,我们也不可能完全去掉MetaData而只能简化一小部分。比如说Param这个表里面的内容就几乎不是必须的,比如说如果你不没有在某个参数上打标签(Attribute),也与COM无关,并且也不期望通过反射来进行参数的查找,那这个函数就可以不产生Param表的一项。至于说方法的签名,你可以说其实不是必须的,但是这个和托管的概念是很有关联的——缺乏这个信息,核心就不能校验你的调用是否给出了正确的参数。在DLL的小版本改进中,随时可能会出现参数多了少了的问题。而是用该Dll的代码在调用的时候,就可能会出现各种莫名其妙的错误,甚至是不容易察觉的错误。

这么仔细想一下,Meta想要瘦身也不是一个容易的事情——要么损失托管的好处,要么优化不了多少。你看看上面的数据,每一项占整个文件的大小都不大,除非你整个的去掉。

当然了,如果你希望能优化点是一点儿,那么我可以给你几个提示:

1、以下的优化你必须自己写工具来处理,你可以利用一个开源的Cecil库来自己裁剪。当然了,某些混淆器也提供这样的功能,但是效果如何没研究过,而且很多时候貌似是以输出一个更大的文件为结果的; 

2、目前的StringHeap输出其实很傻,Enabled属性的getter叫做get_Enabled,是会被输出成两个字符串。但实际上C编码字符串的好处是,你可以指向一个字符串的中间。也就是说,你可以指输出get_Enabled,然后Enabled指向get_之后一个字节;

3、其实一般情况下,私有成员是不希望被使用的,如果你认为无反射需要,其实可以将这部分的成员名称随机指向一个已存在的StringHeap字符串。当然,如果你认为某个类有需要被反射调用使用,那么你可以给该类打上一个特定的标签(Attribute),你的工具则识别并跳过它;

4、ParamTable中大多数项可以裁剪掉,那里面大多数就记一下这个函数中各个参数的名称叫什么,如果你不需要反射并查找参数名称来进行各种匹配,貌似是可以不输出的;

5、不要试图重复利用ParamTable中已存在的项,这样做是徒劳的,因为实际上核心是按照顺序读取的——它加载文件的时候,会有一个指针指向当前使用的Param项,然后根据MethodTable中记录的ParamTable项数来读取若干条记录。总之,别浪费时间尝试用2当中的思路;

6、一个文件中的类、方法、属性等,最好不要超过65535个,一旦超过了,对这些内容的记录就会从2个字节增长为4个字节;

7、减少程序中的字符串常量,比如说,你可以利用反射来读取类和方法的名称时,就不要写一个“Type”这样的字符串,又或者自己用合适的编码比如UTF-8来编码一个文件并进行加载等。

通过使用上述方法,你有可能可以节约大概10%-50%的空间,文件越大效果越不明显。因为当中会有大量的IL代码,这部分你是很难进行优化的,至少你需要改动代码甚至结构。

P.S.:

1、各种资料请自己动手搜索吧,比如搜搜CLI或者UserStringHeap之类的; 

2、那个查看大小的程序,实在对不起,基于某种原因我不可能给大家提供。但是其实你下载一个Cecil的代码,改吧改吧还是很容易实现的。