码迷,mamicode.com
首页 > 编程语言 > 详细

第十四节:线程劫持

时间:2015-04-28 22:48:28      阅读:197      评论:0      收藏:0      [点我收藏+]

标签:

本章前面讨论了垃圾回收期算法。但是,这些讨论有一个很大的前提:仅有一个线程运行,在现实世界,经常会出现多个线程同时访问托管堆的情况,或至少有多个线程同时操作托管堆中分配的对象。一个线程引发垃圾回收时,其它线程绝对不能访问其他任何对(包含他们自己线程栈上的引用),这是因为垃圾回收器可能移动对象,更改其内存地址。

因此,当垃圾回收器想要开启一次垃圾回收时,正在执行托管代码的所有线程都必须挂起。CLR使用几种不同的机制来确保安全的挂起线程,使垃圾回收期可以正常执行,之所以存在多种机制,目的是尽量保持线程运行,并尽量减少开销。本节不讨论这些大量的细节,Microsoft已经做了大量的工作来减少垃圾回收期的开销。随着时间的推移,Microsoft还会继续对这些机制进行改进,以确保高效率的垃圾回收。

CLR想要开始一次垃圾回收时,它会立即挂起正在执行托管代码的所有线程。然后,CLR检查每个线程的指令指针,判断线程执行到哪里。然后,指令指针地址和JIT编译器生成的表进行比较,判断线程正在执行什么代码。

如果线程的指令指针恰好在一个表中标记好的偏移量,就说该线程抵达了一个安全点。线程可以在安全点安全的挂起,直至垃圾回收结束。如果线程的指令指针不再一个内部方法表标记的偏移位置,表明该线程不在安全点,CLR不能执行垃圾回收。在这种情况下,CLR会劫持该线程。也就是说,它会修改线程栈,使返回地址指向CLR内部实现的一个特殊函数。然后,线程恢复运行。当前执行的方法返回后,特殊的函数开始执行,它将线程挂起。

然而,线程有时会长时间的不能从当前方法返回。所以,当线程恢复执行后,CLR会用大约250毫秒的事件尝试劫持线程。过了这个时间,CLR会再次挂起线程,并检查它的指令指针,如果线程以抵达一个安全点,垃圾回收器就可以执行了。但是,如果线程未抵达到一个安全点,CLR就检查是否调用了另一个方法。如果是,CLR再一次修改线程栈,以便从最近的一个方法执行返回后劫持线程。然后,CLR恢复线程,进行下一次劫持。

 

所有线程都抵达安全点或者被劫持后,垃圾回收期才能开始。垃圾回收完成之后,所有线程都会恢复,应用程序继续运行。被劫持的线程返回它们自己的方法。

这个算法还有一个地方需要注意。CLR想开始一次垃圾回收时,会挂起正在执行所有托管代码的线程。但是,正在执行非托管代码的线程不会挂起。所以正在执行托管代码的线程到达一个安全点或者都被劫持之后,垃圾回收期被允许开始。而正在执行非托管代码的线程允许运行,因为他们正在使用的任何对象(在内存中的位置)都应该被固定了,如果正在执行非托管代码的线程返回了托管代码,该线程会被立即挂气,直到垃圾回收完成。

在实际应用中,CLR大多数都是通过劫持线程来挂起线程,而不是根据JIT编译器生成的表来判断是否到达一个安全点。之所以如此,原因是JIT生成的表需要大量内存,而且会增大工作集,进而严重影响性能。所以,如果方法中含有循环,而且循环中没有调用其它方法,JIT编译器生成的表会包含与这些方法有关的信息。相反,如果循环中调用了其他方法,或者完全没有循环,在JIT编译器生成的表中,就不会包含与这个方法有关的多少信息,CLR主要是通过劫持来挂起线程。

第十四节:线程劫持

标签:

原文地址:http://www.cnblogs.com/bingbinggui/p/4464175.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!