做网站用建站模版好还是定制好,西安网站建设总部,搜索引擎优化的作用,免费可商用的图片素材网站虽然看了一些书#xff0c;还网络上的一些博文#xff0c;不过对CLR托管内存细节依然比较模糊。而且因为工作原因总会有很多质疑#xff0c;想要亲眼看到内存里二进制数据的变化。 所以借助winhex直接查看内存以证实书上的描述或更进一步揣摩CLR托管内存的运作方式#xff… 虽然看了一些书还网络上的一些博文不过对CLR托管内存细节依然比较模糊。而且因为工作原因总会有很多质疑想要亲眼看到内存里二进制数据的变化。 所以借助winhex直接查看内存以证实书上的描述或更进一步揣摩CLR托管内存的运作方式这里写下来跟大家一起分享由于自己这方面知识储备不太充足下面的好多内容也是猜测肯定有很对错误希望了解的网友可以帮忙指正 测试环境: windowsXP win10 win7 dotnet4.0 Releases编译 ,下文截图为win7上的运行结果 内存查看工具: winhex 7.5 虽然重点是监测二进制的内存不过基本的测试代码还是要有的测试是直接运行编译好的exe没有使用调试模式编译时要使用Releases因为debug跟Releases在GC回收时对象是否可达的判断是不一样的 下面对内存的查找部分看起来可能有点跳跃因为是借助了反复测试得到的规律很多过程没有赘述 进行之前需要先简单了解CLR对象分配类型对象指针要知道GC的基本过程G0,G1,G2需要简单了解二进制数据的存储主要是大小端 下图是测试中用到的引用对象的结构 下图为测试的主要步骤会分8步进行每一步也都标注出来了 第1监测点 通过TestForGC_3对象里的值类型87564023523 hex 146338F6E3 windows为小端存储所以最后搜索E3 F6 38 63 14 前面的78 41 9B 92 为32位类型对象指针 后面接着的是同步块索引 如果是64位程序这2个数据则都将是64位 根据前面TestForGC_3的地址70354802我们在内存里搜索到了唯一的一个匹配项说明这里一定是张表这个指针指向了TestForGC_3的地址 这个就是 NextObjPrt 了 这个也只是推测根据后面的操作发现新增对象末尾地址总是002BFB 48里面的数据以及之前的反复测试前面的类型指针也是匹配的但测试结果并不是每次这个类型指针都是这个值并且在不同系统版本下差距非常大 后面的操作大家可以看到它的确就是NextObjPrt 整个内存块里存着这个地址的位置也只有这里 第2监测点 按照顺序我们通过内存搜索先找到了a1的地址 这里顺便解析下对象引用类型在内存里的存储 最前面8字节为类型对象指针及同步块索引每个32位如果是64位应用则每个64位 类型对象指针不是一成不变的就是dotnet内置的类型也不能保证这次运行是一个值地址另外一个实例运行起来可能是另外一个地址 这里的地址全部使用偏移地址 后面接着的3个8字节数据发布是TypeA里3个引用类型变量的地址可以看到第2个地址就紧接在下面因为是一起分配的 顺便看下string类型在内存里的存储 根据a1里面的存储的地址02483588轻松定位到了“testtypea”字符串 同样与其他对象一样拥有类型对象指针同步索引块后面有4个字节的数据长度然后后面跟数据 这也的确说明了string千真万确引用类型毫无争议 最后TypeA里面还有一个引用对象TypeB是一样的就不重复说了不过TypeB的指针只存在a1里面即他的回收确实也只能靠根搜索 现在我们通过a1的位置查找内存中含有其地址的内存居然搜索到了5块内存而且都靠的非常近 同样的方法搜索到bytesStart在内存里的地址 同样的结构类型指针同步索引后面跟8字节的长度再后面就是数据 根据地址搜索bytesStart在内存里的指针也只有1个这种结果在同样环境下运行每次的表现都是已有的不过在更换运行环境后就会有明显差异而且也紧靠着a1的指针可以推断他们确实是在一张“表”上 现在看下刚刚找到的NextObjPrt里面的数据是多少02486988下面我们看下这个地址里是什么 可以看到就指向了最后分配的bytesStart地址的后面 每个引用对象后面都有8个全0的字节多次测试反复分析数据都是这样 监测点3 为了验证刚刚的NextObjPrt的确是那块内存 到第3步可以直观的看到bytesThen就直接使用了刚刚NextObjPrt后面指向的内存 同时也看到NextObjPrt指示下的一片内存这个时候对A0 6D 48 02 的搜索也证明内存里只有这么一块存的是这个数据 而且可以看到这个地址确实就是bytesThen后面的内存地址 第4监测点 (重复创建10分份的typeA) 这次直接使用类型对象指针搜索新创建的10份TypeA 可能会搜索出其他数据因为内存里有其他程序及测试前几次运行残留的数据 可以看到这些TypeA直接分配在了bytesThen的后面测试中尽量少使用终端打印。终端打印虽然1行代码不过clr会创建很多对象去完成打印不方便观察 现在想知道这些TypeA的指针却发现内存里根本没有这个地址后面9个的结果一样 甚至连里面的TypeB的指针也搜索不到 其实这些TypeA从一创建即为不可达因为后面再也没有用到它们的地方即一开始就没有任何对象引用过他们在引用跟踪里一直被作为垃圾 这个时候NextObjPrt 已经指向了02487520 监测点5 出RunCreat 出RunCreat这个方法回到RunTest里NextObjPrt指向依然没有变化没有新的对象创建。 监测点6 回收G0 执行完G0回收后 NextObjPrt直接变为了全0 (其实后面还有跟的8个字节的数据也变为了0这8个字节可能为G0阀值) 这里有个疏忽本来先要监测a1的回收现在发现后面的代码残留上一次的测试代码错误的把a1引用了 所以要到这一行结束a1是垃圾 经过G0回收后bytesStart bytesThen应该移动到G1不过看他们在内存里的位置并没有发生任何变化内存里也只有这一份 那10个在RunCreat创建的TypeA也似乎没有什么变化 关于书上的描述跟图例似乎在GC完成后G0向G1的代提升会移动内存不过现在看来并没有移动内存目前GC把85000字节的数据当作大对象所以这里的bytesStart 也不是大对象 那如果bytesStart bytesThen是存活的不能回收那下一个NextObjPrt也一定紧接着在bytesThen后面 整个内存符合条件的也就这么一处(即使是搜索?? 6D 48 02 也只有这一块内存符合条件)虽然这块内存看起来没有什么特别的格式 监测点7 下一个地址从什么地方开始分配 可以看到bytes这个全a的数据真的是从刚刚推测的地址开始分配内存的在RunCreat创建的TypeA也直接被覆盖了确实被当作了垃圾 NextObjPrt现在也正常的指向了bytes的后面 监测点8 数据确实在里面被改动了而且bytesStart bytesThen也的确处于G1 监测点9 这次回收除了a1 其他的bytesStart bytesThen bytes 应该都会被回收 之前放着bytesStart bytesThen bytes 指针的内存 数据已经被覆盖了现在他们都被移动到另外一块内存 而a1 现在应该由g1提升到了g2 之前存放a1指针的地址也全部被覆盖了在内存里搜索到4块新内存其中一块还与前面的bytesStart bytesThen 新指针放在一起 虽然现在NextObjPrt 现在不为0 但也明显被重置了 因为打印的缘故GC后马上创建了新的对象 也很明显并没有覆盖前面的内存而是直接指向了后面的内存 现在来看刚刚被认为是标记GC后下一次分配内存的地址的的内存块现在的地址02488A88这个地址也十分合理正好在两次NextObjPrt 的中间 标识这个地址的确是标记GC后 新NextObjPrt的初始值 监测结束 跳出RunTest马上就执行了一次完全的GC 上面写的比较杂乱虽然很对东西还是没有弄明白也没有发现什么规律不过至少可以得到下面的一些结果 1证明了NextObjPrt 的存在也了解他的基本行为其结构后面数据可能还包含G0阈值等其他数据 2GC回收使用的标记方法的确是根搜索 3被回收的内存不会被擦除只是通过移动NextObjPrt标记下一个内存能被分配的位置 4对象从G0移动到G1内存本身不会移动可能记录对象的指针的表会有相应更新 5不是每次回收都会压缩内存大部分时间都维持原有结构 6对象在内存中的存储细节 最后上面写了那么多其实不单单就是为了看CLR物理内存同样也是表达一种方法用同样的方法也可以查看包括jvm在内的几乎所有进程的物理内存同时winhex不仅可以查看还拥有在运行时直接修改物理内存的能力。 原文地址http://www.cnblogs.com/lulianqi/archive/2017/02/27/6471393.html .NET社区新闻深度好文微信中搜索dotNET跨平台或扫描二维码关注