在物理内存中观察CLR托管内存及GC行为
下图为测试的主要步骤,会分8步进行,每一步也都标注出来了
第1监测点
通过TestForGC_3对象里的值类型87564023523 (hex 146338F6E3 ,windows为小端存储,所以最后搜索E3 F6 38 63 14) 前面的78 41 9B 92 为32位类型对象指针 后面接着的是同步块索引 (如果是64位程序这2个数据则都将是64位) 根据a1里面的存储的地址02483588,轻松定位到了“testtypea”字符串 同样与其他对象一样拥有类型对象指针,同步索引块,后面有4个字节的数据长度,然后后面跟数据 这也的确说明了string千真万确引用类型,毫无争议 最后TypeA里面还有一个引用对象TypeB,是一样的就不重复说了,不过TypeB的指针只存在a1里面(即他的回收确实也只能靠根搜索) 到第3步,可以直观的看到bytesThen就直接使用了刚刚NextObjPtr后面指向的内存, 同时也看到NextObjPtr指示下的一片内存(这个时候对A0 6D 48 02 的搜索也证明内存里只有这么一块存的是这个数据) 而且可以看到这个地址确实就是bytesThen后面的内存地址第4监测点 (重复创建10分份的typeA) 出RunCreat()这个方法回到RunTest()里,NextObjPtr指向依然没有变化(没有新的对象创建)。
监测点6 (回收G0) 那如果bytesStart bytesThen是存活的,不能回收,那下一个NextObjPtr也一定紧接着在bytesThen后面 整个内存符合条件的也就这么一处(即使是搜索?? 6D 48 02 也只有这一块内存符合条件),虽然这块内存看起来没有什么特别的格式
监测点7 (下一个地址从什么地方开始分配) 可以看到bytes这个全a的数据真的是从刚刚推测的地址开始分配内存的,在RunCreat创建的TypeA也直接被覆盖了(确实被当作了垃圾) NextObjPtr现在也正常的指向了bytes的后面
监测点8 之前存放a1指针的地址也全部被覆盖了,在内存里搜索到4块新内存,其中一块还与前面的bytesStart bytesThen 新指针放在一起 现在来看刚刚被认为是标记GC后下一次分配内存的地址的的内存块,现在的地址02488A88,这个地址也十分合理,正好在两次NextObjPtr 的中间 标识这个地址的确是标记GC后 新NextObjPtr的初始值
监测结束
跳出RunTest,马上就执行了一次完全的GC上面写的比较杂乱,虽然很对东西还是没有弄明白也没有发现什么规律,不过至少可以得到下面的一些结果
1:证明了NextObjPtr 的存在,也了解他的基本行为(其结构后面数据可能还包含G0阈值等其他数据)
2:GC回收使用的标记方法的确是根搜索
3:被回收的内存不会被擦除,只是通过移动NextObjPtr标记下一个内存能被分配的位置
4:对象从G0移动到G1,内存本身不会移动(可能记录对象的指针的表会有相应更新)
5:不是每次回收都会压缩内存,大部分时间都维持原有结构
6:对象在内存中的存储细节
最后上面写了那么多,其实不单单就是为了看CLR物理内存,同样也是表达一种方法,用同样的方法也可以查看包括jvm在内的几乎所有进程的物理内存,同时winhex不仅可以查看,还拥有在运行时直接修改物理内存的能力。