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

推荐订阅源

C
Cisco Blogs
Schneier on Security
Schneier on Security
T
Tor Project blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
T
Tenable Blog
C
Cyber Attacks, Cyber Crime and Cyber Security
T
Threat Research - Cisco Blogs
C
CERT Recently Published Vulnerability Notes
Security Latest
Security Latest
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
NISL@THU
NISL@THU
L
Lohrmann on Cybersecurity
Scott Helme
Scott Helme
Webroot Blog
Webroot Blog
Project Zero
Project Zero
Google Online Security Blog
Google Online Security Blog
The Last Watchdog
The Last Watchdog
Spread Privacy
Spread Privacy
Hacker News: Ask HN
Hacker News: Ask HN
PCI Perspectives
PCI Perspectives
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
W
WeLiveSecurity
Attack and Defense Labs
Attack and Defense Labs
D
Darknet – Hacking Tools, Hacker News & Cyber Security
N
News | PayPal Newsroom
Help Net Security
Help Net Security
The Hacker News
The Hacker News
H
Heimdal Security Blog
O
OpenAI News
S
Security @ Cisco Blogs
N
News and Events Feed by Topic
Cyberwarzone
Cyberwarzone
Simon Willison's Weblog
Simon Willison's Weblog
G
GRAHAM CLULEY
www.infosecurity-magazine.com
www.infosecurity-magazine.com
博客园 - 叶小钗
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Hacker News - Newest:
Hacker News - Newest: "LLM"
T
Tailwind CSS Blog
大猫的无限游戏
大猫的无限游戏
A
Arctic Wolf
I
Intezer
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
S
Security Affairs
P
Proofpoint News Feed
S
Secure Thoughts
腾讯CDC
Google DeepMind News
Google DeepMind News
量子位
罗磊的独立博客

博客园 - Sumtec

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

博主您好! 
首页是发表精品文章的地方。 
您的博文“QUIZ:一个有8个属性的匿名类大约会占多大的文件大小?”被移出首页,由此给您带来的麻烦,请谅解! 
首页文章要求:原创,排版整齐,文中有文字明确说明文章的主题,内容对程序员有帮助。 
下列类型的文章不允许发到首页: 

1) 转载;2) 只有代码;3) 简单的提问;4) 软件发布;5)人才招聘;6) 包含推广或广告内容;7)活动信息;8)关闭评论功能的随笔;9)不完整的内容。

既然抛砖引玉中的抛砖不能出现在首页,那我也不想引玉了。真遗憾,在博客园中用了这么长的时间,我终于有了当年从CSDN退出时的感觉:实在无法理喻这里的风气了。难道C#自己写的一个自定义分页控件就是一个很有价值的文章?我的意思并非这样的文章就不好,而是说,一个引起思考的短文居然和这个比都没有价值,我不知道这样的地方价值在哪里?或者说,这样的地方已经没有多少营养了,需要认真考虑另起炉灶了。

我想问一下,认为不值得放首页的那些个同学,你们有几个能回答出我说的这个问题?你们有几个自己真的做过这方面的研究?知道MetaData都包含什么?这个是一个简单的提问?

前阵子微博上还有人调侃,说:

 

其实我倒不是觉得不能写一些非技术的东西,或者制造一些话题。但是,现在真不是观众说了算,一些我觉得没啥看头的东西占据了首页,一些其实蛮有看头的东西却被大量水文瞬间冲掉。好,废话不多说,最后给出这个被强制移出首页的QUIZ的答案。

一个包含了8个属性的匿名类,会占用大约5K的文件存储空间。也就是说,如果你使用了10个这样的匿名类,你的文件大小就会导致你的文件增大大约50KB。对于一个桌面应用来说,50KB算不上什么,但对于一个SilverLight应用来说,这就不是一个小数目了。更可怕的是,如果我们不知道这个问题,使用了一个比如说42个属性的匿名类,就会导致你的文件增长大约46KB的大小。当然,这里有严重的水分,原因后面会简单提到。

在开始进行一些简单的分析之前,也许你需要自己去了解一些有关CLI的知识,比如什么是BlobHeap、UserStringHeap、StringHeap、TableHeap等,以及里面是用什么格式进行组装的。原本呢,我觉得这些大家只要用Google搜索一下CLI和MetaData就会出来了,可是我现在觉得,以博客园的平均水平而言,也许连这个能力都不具备。那么好吧,下面这个是链接,有能力的请自行阅读:

http://download.microsoft.com/download/d/c/1/dc1b219f-3b11-4a05-9da3-2d0f98b20917/partition%20ii%20metadata.doc

下面,我们来对这一个问题进行一下简单的剖析。为了让问题更明显和突出,我们对一个有42个属性的类进行分析。

 

首先,整个匿名类哪些部分会占用的比较多? 根据统计,在TableHeap中使用了5k,StringHeap中使用了7k,Blob使用了27k,UserString使用了1k,Body占用了近5k。需要说明的是,除了Body部分(IL代码)相对比较准确之外,其它的部分的统计是不准确的。这是因为根据规范,相同的内容很可能会自行排重而只记录一遍。而同一个内容在整个程序集中会多次被使用到,据一个例子:比如属性的访问器名称get_PageId,可能在多个类当中都有该属性;此外还有其它很多原因可能会导致重复统计。根据我的估算,可能需要打个3折,即便如此也得占用大约14k的空间。为了便于讨论,我们这里就先忽略这些重复的统计。

途中的Blog占用空间非常大,至于为什么,这是我尚未解开的部分。也许是因为:

1、BlobHeap包含了太多的东西,比如一个函数的签名,标签(Attribute)所使用的具体参数等;

2、使用的场景也比较多,比如TableHeap中的MethodRef、Method、Param等等,几乎各个Table都可能有指针指向Blob。甚至连MethodBody当中的某一句IL,比如call System.Linq.Quearyable.Where'1 ... 等,都可能会在Blob里面加一些东西;

3、从Reflector看到的情况无法解释这一部分异常增大的原因,甚至不排除这个工具本身哪里有Bug导致统计数据出现了错误。

但无论如何,也不是重复统计所能解释的,关于这部分后面会给出两个图进行说明。

抛开奇怪的Blob部分,还有一些很容易发现的问题。比如,为什么一个匿名类需要使用1k的UserStringHeap呢?UserStringHeap中记录的是你代码当中的字符串常量,比如说下面这么一段代码:

void Main()
{
  Console.WriteLine(
"Hello world!");

这么一个语句,当中的"Hello word!"就是要进入UserStringHeap的,大约会占用23个字节。可是我们的匿名类里面,怎么会有字符串常量呢?原来,编译器在生成匿名类的时候,为了便于你调试,会在类的前面打上一个DebuggerDisplayAttribute标签,比如:(为避免泄露些什么,字段名称已经修改,字符串中的...表示后面还有好长好长……)

DebuggerDisplay(@"\{ PageId = {PageId}, ReadId = {ReadId}, RefId = {RefId}, Guid = {Guid}, TypeId = {TypeId}, CategoryId = {CategoryId}, Title = {Title}, Mode = {Mode}, Setting = {Setting}, Tags = {Tags} ... }", Type="<Anonymous Type>")]

打上这一个标签的好处是,当你进行断点调试的时候,你可以看看这个匿名类里面的属性值都是什么。可正是这个标签,导致了UserString的占用。由于不同匿名类中,属性名称可能会不一样,就算一样,顺序也可能不一样,因此这串字符串也就不太可能完全相同。于是,你用的匿名类越多,这种无谓的占用就会越多。幸好,这个问题只会出现在Debug的编译结果中,对于Release发布则没有这个标签。

接下来,也许你会奇怪,对于一个42个属性的匿名类,所使用的StringHeap会达到7k,好吧,这是我的工具重复统计导致了过分的放大。但是,仔细看一下匿名类你就会发现:

1、一个有着N个属性的匿名类实际上是一个有N个泛型参数的泛型类。假设有一个属性是PageId,则:

2、属性名称叫做PageId;(7个字节,注:C字符串格式最后有一个字符0)

3、属性的访问器叫做get_PageId;(11个字节)

4、属性所对应的成员叫做<PageId>i__Field;(17个字节)

5、属性的泛型参数名称叫做<PageId>j__TPar;(16个字节)

6、匿名类名称为AnonymousType#`N,数字#表示第#个匿名类,数字N表示有N个属性。那么对于有着8个属性的匿名类1,长度就是17个字节,对于有着42个属性的匿名类2,长度就是18个字节;

7、假设我们属性名称的平均长度就正好是6个,那么42个属性的匿名类就至少占据了2k有多。

从上面这个部分,我们就可以发现,假如我们把几乎不会影响一般运行,甚至对反射也没有太大影响的成员名和泛型参数名优化一下,变成平均4个字节(甚至是0字节),那么也可以减少超过一半以上的空间占用。对于使用匿名类较多的某个dll来说,光是这部分可能就可以优化掉大约10k左右的大小。

另一个让人吃惊的地方,是MethodBody占用非常大,大5k之多,平均一个属性有大约100多个字节。要知道,一个如下的属性:

public int PageId
{
  
get
  {
     
return _pageId;
  }

对应的il也就如下三句:

ldarg.0
ldfld thisType._pageId;
ret

 共6个字节。如果是Debug编译,会多出额外3句以便于调试,也就在多4个字节而已。换而言之,有其它的函数在哪里捣鬼,捣鬼的那几个函数分别是:

Equals、GetHashCode、ToString以及构造函数.ctor。

 

如果我们用Reflector打开这个函数来看,Equals、GetHashCode、ToString以及构造函数都要访问到每一个属性。其中对于构造函数来说,这是几乎不可避免的,因为需要对匿名类的每一个属性进行赋值操作。但是为啥需要重写Equals等其他三个函数呢?这是因为这些对象可能会被用到Dictionary的Key中,此时就必须重写Equals、GetHashCode、ToString这三个函数,而这三个函数加起来就得占用大约4k的大小。准确说,所有匿名类的属性总数,决定了整个dll中该部分代码的大小。如果你这个dll中一共有10个匿名类,每个大约10个属性,那么光是代码部分,这三个函数就占用了大约10k的大小。不要忘了,除了代码之外,我们还需要因此写入一些元数据,以及为数不少的UserString。而实际上用到这三个函数的几率非常非常的小,完全可以通过实时动态Emit来完成,而不需要占用这么大的代码空间。当然了,进行动态生成的代码也会占用不少的空间,但如果你做的是一个很大的项目,比如你有很多的页面,每个页面用到不同的Dll,里面都有不少的匿名类,那么这点的优化成本很可能是值得的。而另一种方式也许更好,那就是假设该类不会被当作对比的Key来使用,而是当作一个普通的类,那你可以干脆裁减掉这三个方法(这种裁剪我还没有试验过,也许你需要自己进行尝试)。

最后,我们来看看Blob方面的一个奇怪的问题,那就是:匿名类的属性越多,则某一个属性需要用到的Blob就越大。比如说对于一个有42个属性的匿名类,其某个属性访问器所使用用到的Blob大小如下图所示:(包括方法签名,方法中调用某函数、使用某成员所产生的一个MemberRef记录所用到的方法签名等)

 

而一个只有8个属性的匿名类,其某个属性访问器所使用到的blob大小为:

 

目前我想到的合理解释是,由于需要返回该属性对应的字段,如下述IL代码:

ldfld !0 <>f_AnonymousType0'42<!<xxx>j_TPar, !<xxx>j_TPar, ...>::<PageId>i__Field 

这里需要产生一个针对该字段<PageId>i__Field的签名,而签名当中又带有当前类的各种泛型参数信息,因此造成了属性越多,占用Blob越厉害的结果。而至于这部分的Blob是否如我猜测,如是,是否会每个参数都要占用这么多还是其中一部分会被重复利用,都尚未可知。当然,也有可能我的程序有Bug造成的。关于此,我想我不会在博客园继续写了。

如果有兴趣的同学,可以去下载一个开源的项目,叫做Mono.Cecil,我的工具就是在这个项目的基础上去完成的。通过该项目的源代码,你可以很好的了解整个.NET文件的结构组成。当然,在你正式开始阅读这部分代码之前,最好先看看我前面提到的那篇文章,因为这个Mono.Cecil的项目里面,注释量基本是Zero。对于那些从来不喜欢Read the fuck code的同学,会是一种巨大的挑战。

最后,我宣布从博客园正式退役了,不玩了。有些东西抱怨太多就没意思了,每次发布都要头痛,到底是放首页呢还是放首页呢,还是放首页呢?然后要从一堆花花绿绿的各种我都不知从何下手的选项中,选一些我知道的不知道的东西,简直就是一场噩梦。

关键是,没错,我很不爽,你们把我觉得还比较有难度有挑战的小提问给挪出去了,你们不提倡思考了,开始喜欢浮躁喜欢造话题。那些相对来说没啥技术含量的东西,字数也不见得多出多少,不也照样放在首页吗?既然咱们的人生观价值观已然发生分歧了,那只好分道扬镳了。

我刚刚做了一个非常艰难的决定,那就是博客园,我不玩了。至于你们信不信,我反正是信了。走咯,回家了,拜拜了各位!