工具出现挂死问题
1.问题描述
工具出现挂死问题,巡检IIS发现以下异常日志
现网系统日志:
事件类型: 错误
事件来源: .NET Runtime
描述:
Application: DiyRingSet30Tool.exe
Framework Version: v4.0.30319
Description: The process was terminated due to an internal error in the .NET Runtime at IP 791F7E06 (79140000) with exit code 80131506.
说明:此日志可以通过“开始”-“所有程序”-“管理工具”-“事件查看器”-“应用程序”,观察类型为错误或者警告的日志,一般出现错误日志都是应用程序错误导致的,请引起重视
2.修复方法
在工具的配置文件中新增以下红色的配置节点内容
<configuration>
<runtime>
<gcConcurrent enabled="false"/>
</runtime>
</configuration>
3.问题原因
原因:正在引用的对象被取消引用由垃圾回收器在公共语言运行库4(CLR4)中发生的问题。因此,垃圾回收或应用程序尝试访问已发布的对象的过程中发生访问冲突。
4.相关知识说明
垃圾回收器可自行优化并且适用于多种方案。 可基于工作负荷的特征使用配置文件来设置垃圾回收的类型。 CLR垃圾回收可以做内存移动也可以不做移动。不做移动时也称为“清扫”,清扫的代价要比做压缩的代价低一些,因为他不需要复制移动内存。CLR 提供了以下类型的垃圾回收:
4.1运行时GC工作模式
1)工作站模式:关闭并发的工作站GC、开启并发的工作站GC
2)服务器模式:服务器GC
工作站垃圾回收,用于所有客户端工作站和独立 PC。 这是运行时配置架构中的 <gcServer> 元素的默认设置。既可以是并发的,也可以是非并发的。 并发垃圾回收使托管线程能够在垃圾回收期间继续操作。从 .NET Framework 4 开始,后台垃圾回收取代了并发垃圾回收。
服务器垃圾回收,用于需要高吞吐量和可伸缩性的服务器应用程序。 服务器垃圾回收既可以是非并发或后台运行。
4.2各个GC模式的设计目标
1) 关闭并发的工作站GC为高性能服务器的高吞吐量做了优化。我们在垃圾回收时根据分配和复活模式做动态调优因此可以程序运行时自动调优GC的工作效率。
2) 开启并发的工作站GC是为要求精确响应时间的交互式应用程序设计的。开启并发使垃圾回收造成的工作进程暂停时间缩短。 这个目的是用一些内存和CPU换来的,因此在这种模式下垃圾回收需要做的工作略多一点需要的回收时间会略长一些。
3) 服务器GC,从名字上我们可以看出这种工作模式是为服务器应用程序设计的;典型的场景是你有一个工作线程池这些线程做着相似的处理。例如:做处理同样的请求或者处理相同类型的事务。所有的线程使用几乎相同的分配模式。服务器GC是为要求高吞吐量的和高扩展性的多处理器服务器设计的
4.3各个GC模式如何工作
工作站模式(workstation mode) 。
1)这种模式下GC假设机器上运行的其他应用程序对CPU资源要求不高,并发模式是默认的工作站模式,该模式下垃圾回收器分配一个额外的后台线程在应用程序运行时并发回收对象。一个线程因为分配对象造成第0代超出预算时,垃圾回收器挂起所有线程(不会挂起运行本机代码的线程、并发模式允许托管线程在回收期间运行),判断需要回收哪些代,如果需要回收第2代,就会增加第0代的大小。然后应用程序恢复执行。
2)回收发生在触发垃圾回收的用户线程上,并保留相同优先级。 因为用户线程通常以普通优先级运行,所以垃圾回收器(在普通优先级线程上运行)必须与其他线程竞争 CPU 时间。
3)工作站垃圾回收始终用在只有一个处理器的计算机上,而不管 <gcServer> 设置如何。 如果你指定服务器垃圾回收,则 CLR 会使用工作站垃圾回收,并禁用并发。
服务器模式 (servermode)。
1)这种模式下GC假设机器上没有运行其他应用程序,所有的CPU都可以用来进行垃圾回收操作。在这种情况下虚拟内存按照CPU数量划分区域分开对待,每个CPU上都运行一个GC线程负责回收自己的区域。 为每个 CPU 提供一个用于执行垃圾回收的一个堆和专用线程,并将同时回收这些堆。 每个堆都包含一个小对象堆和一个大对象堆,并且所有的堆都可由用户代码访问。不同堆上的对象可以相互引用。
2)回收发生在以 THREAD_PRIORITY_HIGHEST 优先级运行的多个专用线程上。
3)因为多个垃圾回收线程一起工作,所以对于相同大小的堆,服务器垃圾回收比工作站垃圾回收更快一些。
4)服务器垃圾回收通常具有更大的段。
5)服务器垃圾回收会占用大量资源。 例如,如果在一台具有 4 个处理器的计算机上运行了 12 个进程,则在它们都使用服务器垃圾回收的情况下,将有 48 个专用垃圾回收线程。 在高内存加载的情况下,如果所有进程开始执行垃圾回收,则垃圾回收器将要计划 48 个线程。
并发垃圾回收
让我们从关闭并发的工作站GC说起,其执行流程如下:
1) 一个托管进程做内存分配
2) 分配完所有可用的内存
3) 触发了垃圾回收,垃圾回收操作在做分配的线程上运行
4) GC调用SuspendEE来挂起所有的托管线程
5) GC开始工作
6) GC调用RestartEE来重启工作托管进程
7) 托管进程继续运行
图:演示了在单独的专用线程上执行的并发垃圾回收
你可以看到在第5步中所有的托管线程都停止执行来等待垃圾回收完成工作。SuspendEE不会挂起本地线程(native threads)。
在开启并发垃圾回收(Concurrent GC)时,最大的差异是挂起和重启。并发垃圾回收通过最大程度地减少因回收引起的暂停,使交互应用程序能够更快地响应。 在运行并发垃圾回收线程的大多数时间,托管线程可以在回收期间继续运行。 这意味着并发GC暂停时间非常少。
因此开启并发的垃圾回收会尽可能少的执行垃圾回收,执行时间也非常短。在剩余的时间中如果需要托管线程可以运行和分配内存。开启并发时我们会在开始时给0代一个很大的分配预算来保证垃圾回收运行期间有足够的空间分配对象。(GC中的每一代都有一个“分配预算”的概念。每一代的“分配预算”在运行时是动态调整的。因为我们经常在0代上做分配,你可以想象0代预算超支了,这样就会触发垃圾回收。这里的预算和GC堆的段大小完全不是一回事,预算比段大小要小得多。)
尽管如此,如果在并发回收运行中托管线程需要分配过多的内存,线程也会被堵塞直到回收完成。(在并发垃圾回收期间在堆上为小对象分配空间的能力受到在并发垃圾回收启动时临时段上保留的对象的限制。 一旦到达临时段的末尾,将必须等待并发垃圾回收完成,同时将挂起需要执行小对象分配的托管线程。)
在并发垃圾回收中,允许托管的线程在回收期间运行,记住0代和1代回收非常快,所以此选项只影响2代回收,不影响0代和1代,即在做0,1代回收时是不会做并发回收的。只是在2代回收时才会并发回收。
并发垃圾回收具有一个稍微大点的工作集(与非并发垃圾回收相比),这是因为你可以在并发回收期间分配对象。 但是,这会影响性能,原因是分配的对象将会成为你的工作集的一部分。 实质上,并发垃圾回收会牺牲一些 CPU 和内存来换取更短的暂停。
后台工作站垃圾回收
后台垃圾回收只在 .NET Framework 4 及更高版本中可用。 在 .NET Framework 4 中,仅支持工作站垃圾回收。 从 .NET Framework 4.5 开始,后台垃圾回收可用于工作站和服务器垃圾回收。
图:对工作站上的独立专用线程执行的后台垃圾回收
后台垃圾回收期间对暂时代的回收称为前台垃圾回收。 发生前台垃圾回收时,所有托管线程都将被挂起。当后台垃圾回收正在进行并且你已在第 0 代中分配了足够的对象时,CLR 将执行第 0 代或第 1 代前台垃圾回收。 专用的后台垃圾回收线程将在常见的安全点上进行检查以确定是否存在对前台垃圾回收的请求。 如果存在,则后台回收将挂起自身以便前台垃圾回收可以发生。 在前台垃圾回收完成之后,专用的后台垃圾回收线程和用户线程将继续。
在后台垃圾回收中,在进行第 2 代回收的过程中,将会根据需要收集暂时代(第 0 代和第 1 代)。
后台垃圾回收无法设置;它会自动运行并启用并发垃圾回收。 后台垃圾回收是对并发垃圾回收的替代。 与并发垃圾回收一样,后台垃圾回收是在一个专用线程上执行的并且只适用于第 2 代回收。
后台垃圾回收可以消除并发垃圾回收所带来的分配限制,因为在后台垃圾回收期间,可发生暂时垃圾回收。 这意味着,后台垃圾回收可以移除暂时代中的死对象,而且还可以在第 1 代垃圾回收期间根据需要展开堆。
后台服务器垃圾回收
服务器垃圾回收,这种模式和工作站模式完全不同。我们会为每一个CPU创建一个回收线程。垃圾回收在这些线程上执行而不是在分配线程上,其工作流程如下:
1. 一个托管线程做回收
2. 分配达到阀值
3. 给GC线程发信号,让GC线程做垃圾回收,等待回收结束
4. GC线程运行,结束时发出回收完成的信号(在回收过程中,所有的托管线程会像工作站模式中一样被挂起)
5. 托管线程收到信号重新开始运行
从 .NET Framework 4.5 开始,后台服务器垃圾回收是服务器垃圾回收的默认模式。 若要选择此模式,请在运行时配置架构中将 <gcServer> 元素的 enabled 特性设置为true。 此模式与后台工作站垃圾回收具有类似功能,但有一些不同之处。 后台工作区域垃圾回收使用一个专用的后台垃圾回收线程,而后台服务器垃圾回收使用多个线程,通常一个专用的线程用于一台逻辑处理器。 不同于工作站后台垃圾回收线程,这些线程不会超时。
图:对服务器上的独立专用线程执行的后台垃圾回收。
4.4如果配置各个垃圾回收模式
要关闭并发回收,在配置文件中添加下面配置项:
<configuration>
<runtime>
<gcConcurrent enabled="false"/>
</runtime>
</configuration>
要使用服务器GC,使用下面配置:
<configuration>
<runtime>
<gcServer enabled=“true"/>
</runtime>
</configuration>
5.应用场景说明
1)如果你在写一个独立的托管程序并且没有做任何配置,默认情况下CLR 将运行工作站垃圾回收并启用并发垃圾回收。 对于单处理器计算机和多处理器计算机都是如此。这会降低性能
2)如果应用程序是单线程并且涉及大量的用户交互,请开启并发垃圾回收,即不需要在配置文件中修改回收方式,便于应用程序不会因为执行垃圾回收而暂停
3)如果你的程序在宿主程序中运行,宿主可能会为程序选择GC的工作模式。
4)如果运行应用程序的数百个实例,请考虑使用工作站垃圾回收并禁用并发垃圾回收。 这可以减少上下文切换,从而提高性能。
5)并发回收是为了给用户更好的交互体验,适合客户端应用程序,但是同时要注意并发回收对性能有损害,使用更多地内存。
6)若要在运行多个进程时提高性能,请禁用并发垃圾回收。
7)在工作站或服务器垃圾回收中,你可以启用并发垃圾回收,以便在大多数回收期间,让各线程与执行垃圾回收的专用线程并发运行。 此选项只影响第 2 代中的垃圾回收;第 0 代和第 1 代中的垃圾回收始终是非并发的,因为它们完成的速度非常快。
6.参考网站
1)http://q.cnblogs.com/q/45625/
2)http://msdn.microsoft.com/zh-cn/library/at1stbec.aspx
3)http://www.cnblogs.com/yukaizhao/archive/2011/11/29/use-gc-effective-2.html
4)http://technet.microsoft.com/zh-CN/library/ee851764
5)http://blog.csdn.net/directionofear/article/details/8034133
6)http://msdn.microsoft.com/zh-cn/library/ee787088(v=vs.110).aspx