您好、欢迎来到现金彩票网!
当前位置:老k棋牌 > 栈上托 >

垃圾回收机制是不是这样理解?

发布时间:2019-06-10 16:13 来源:未知 编辑:admin

  首先CLR规定所有的资源都从托管堆中分配。此托管堆维护对象资源,会为我们自动管理对象状态。

  进程初始化时,CLR会预留一块连续的地址空间即托管堆,但是没有对应的物理存储空间。此托管堆上维护者一个指针,它指向下个对象在托管堆中的分配位置。

  new操作符会生成一个IL指令newobj,指令会指导CLR进行以下工作。

  C、CLR检查保留区域是否能够提供分配对象所需的字节数,如果有就提交存储。对象会在指针NewObjPtr指向的位置放入,为对象分配的字节数清零,并调用实例化构造器返回对象的地址。

  托管堆上分配对象的前提是空间内存足够,那就引出了下一个机制垃圾回收机制,来回收内存,释放资源。

  定义:垃圾回收器会在回收内存之前执行Finzlize()(如果此类实现了Finalize方法),垃圾回收会自动调用此方法。垃圾回收器会因隐式执行。

  原理:一个实现了Finalize方法的对象new之后,会在垃圾回收器维护的一个终结列表中添加一个指针指向这个新分配的这个对象。在回收内存之前调用它的Finalize方法。

  不足:Finalize释放的是托管资源,垃圾回收器会在进行垃圾回收的时候释放托管资源,我们不确定下次的垃圾回收发生在何时,我们想自己控制垃圾回收并控制菲菲托管资源的释放。

  所以下面就使用显示摧毁资源状态(前提是确定不再使用,确认需要关闭)。比如:数据库连接、文件读写

  这里的资源释放统一处理 Dispose(Boolean disposeing) 方法,如果参数为true,会标记此对象资源显示关闭,但是没有终结,可以正常访问字段。如果是false,那就终结对象,回收内存。

  因为继承了IDispose接口,所以要实现一个无参的Dispose()方法,而我们经常使用的Close()是因为出于习惯觉得有一个叫Close()的方法似乎更亲切。所以就添加了一个Close()方法。没有其他特殊用途。

  当我们调用Dispose()或者Close()方法时,对象本身的内存还没有释放,仍然需要垃圾回收器来回收内存。

  垃圾回收器检查托管堆中是否有应用程序不再使用的对象。有,回收内存(如果回收后,内存仍然不够,就抛出内存溢出异常)。

  每个应用程序都包含一组根,每个根都是一个存储盒子,里面包含着引用对象指针(要么引用一个对象,要么为null)。

  例如类中定义的任何静态字段会被认为有一个根,方法中的任何参数和局部变量也会被认为有一个根。只有引用类型的变量才被认为是根。

  JIT在生成CPU代码的同时还会生成方法在本地CPU指令中的一个字节偏移范围的记录项,这个记录项也包含着根的一组内存地址和CPU寄存器。

  上面的类在第一次调用方法 WriteBytes 的时候,JIT会将IL代码翻译成CPU指令,如下(x86 CPU):

  ebx在偏移到00000003处开始为寄存器的根,到循环结束00000028处结束根。此类为实例,所以会有一个this指针,通过,后面的ecx寄存器传递,并存储到前面的寄存器ebx。

  同样,esi在偏移到00000005处开始为寄存器的根,直到00000028根结束。它通过寄存器edx传递bytes[],并将数组存入到寄存器esi。

  (1)这三个寄存器中引用指向的对象都是根,而且这些根中所引用的堆中的对象也不能回收。

  (2)其次垃圾回收器会检查线程栈上行,检查每个方法的内部表来确定所有调用方法的根。

  (1)垃圾回收器开始执行时,它会假设堆中的所有对象都是垃圾。它会假设线程栈和堆没有引用关联,没有CPU寄存器引用堆中的对象。也没有静态字段引用堆中的对象。

  (2)接着进入标记阶段,沿着线程栈上行检查所有根,如果发现一个根引用了一个对象,就对这个对象进行标记(同步块索引字段上开启一个bit=1的标识)。

  收集根并标记完后,会有标记和未标记的对象。标记的就是程序可以继续访问的,反之就是不可达的垃圾。就会对垃圾进行回收内存。

  (1)垃圾回收器会线性遍历堆,遇到垃圾对象时,检查一下连续内存块,如果较小就忽略,较大就会将非垃圾对象移动到这里。

  (2)但是非垃圾对象之前的地址和寄存器等都会失效,垃圾回收器也会重新访问根,生成新的地址等等。

  我们的托管堆初始化时不会包含任何对象,我们初始化的对象会添加到托管堆上,这些对象我们称为第0代。第0代的存储上限为256KB,第一代为2M

  此时我们标记第0代对象在堆上的存储上限为256KB(只是假设一下),如下图:

  5个对象A、B、C、D、E。程序运行一会之后,C和E变得不可达,等待垃圾回收器来回收内存。

  每当第0代满时,也就是当前堆中的对象达到了上限256KB,这时垃圾回收器开始执行垃圾回收,

  如果此时分配新的对象F时,出现A~E达到分配上限256KB,垃圾回收器就会压缩D使得和之前可用内存连续起来。

  运行一段时间,B、H、J也是不可达状态,同时在新分配L时第0代达到了上限256KB,如下图:

  现在第0代满,垃圾回收器运行,它会压缩I、K的内存与G连续。同时回收H、J。

  此时第一代这个虽然有不可达的B对象,但是第一代没有达到上限2M,垃圾回收器就不会对B进行回收。

  回收后:我们看到没有对B进行回收,那是因为第一代没有达到上限2M,这一切为了性能,因为第一代中出现垃圾的频率一般远远低于第0代甚至没有垃圾。

  这样垃圾回收器就宁愿让垃圾暂时呆在那里,暂时不去回收。这样节省了时间,增加了效率。

  程序依然运行,就会有更多的对象分配到堆上,同时产生更多的的垃圾,如下图:

  此时垃圾回收器检测到第一代也超过限额2M,就检测第一代中的对象进行垃圾回收。知道这个时候第一代的垃圾才有幸回收。

  回收后:此时会将第一代剩下的升级到第二代中,原来在第0代存活的对象,也会升级到第一代中。

  PS:在CLR初始化时,会对第0代、第一代、第二代进行内存限额设定:256KB、2M、10M。限额越大执行垃圾回收的频率越低。

  只在第0代满时进行垃圾回收,当第0代满,此时第一代也满,才会对第一代进行垃圾回收。所以效率上是可以保证的。

  当CLR检测到回收第0代对象后,几乎没有回收多少内存,此时就会调整上限到512KB。同样如果回收的垃圾很多,那调整到128KB。以此类推,自动调剂。

http://drpetermitoff.com/zhanshangtuo/11.html
锟斤拷锟斤拷锟斤拷QQ微锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷微锟斤拷
关于我们|联系我们|版权声明|网站地图|
Copyright © 2002-2019 现金彩票 版权所有