zoukankan      html  css  js  c++  java
  • .Net 调式案例—实验4 高CPU(High CPU)回顾

    转载: https://www.cnblogs.com/softfair/archive/2008/03/03/dotnet-Debugging-Demos---Lab-4-HighCPU--Review.html

     

    .Net 调式案例实验CPUHigh CPU)回顾

     

    现在开始第四个实验。

    前面的案例和设置指导

    请参看前面发布的文章。

    再现问题

    1)  重新启动IISiisreset

    2)  浏览http://localhost/BuggyBits/ AllProducts.aspx,这大概会花费5秒或更多的时间,这取决于你的机器性能。

    3)  打开页面的过程中,看看任务管理器中的cpu使用量。

    Q:在这个请求的处理过程中,cpu的使用量是怎么样的?

    A:很高,接近100%

    设置性能计数器日志

    1)  打开一个性能计数器,从开始/运行 中输入:perfmon.exe

    2)  右键在点击“性能日志和警告/日志计数器”上点击“新建日志设置”,在弹出的对话框中输入:Lab3-Mem

    3)  在接下来弹出的对话框中点击“添加对象”。

    4)  添加对象“.NET CLR Memory”和“Process”,点击关闭。

    5)  把时间间隔设置成1秒,因为我们这个例子运行的时间比较短。

    6)  修改运行方式,把“<默认>”修改成“domainusername(把默认这个东西删除掉,把你机器的用户名输入进去),然后点击设置密码,把你的用户名对应的密码输入进去。这样做的目的是你要以管理员(administrator)或具有对w3wp.exe 调试权限的用户来运行日志计数器。否则将得不到.net 计数器的值。

    7)  点击确认,这个将会启动性能监视器对进程的监控。

    准备收集内存dump文件

    1)打开一个命令窗口,切换到调试器目录,输入如下的命令,但不要敲回车:

       adplus -hang -pn w3wp.exe -quiet

    再现问题并收集dump文件

    1)在命令提示符下,切换到TinyGet目录下,输入命令,启动5个线程,每次1个请求。
        tinyget -srv:localhost -uri:/BuggyBits/AllProducts.aspx -threads:5 -loop:1

    2)当cpu很高的时候,在adplus 的命令中 敲下回车键。

    3)停止性能监视器。

    打开dump文件并看看里面正在做什么

    步骤和前面实验中讲的一样,这里略过。

    检验一下你是在恰当的时候获取了这个dump文件

    1)  运行 !threadpool 来看看cpu的使用量,确信你在恰当的时候抓取到了这个dump文件。

    Qcpu的使用率是多少?

    A:根据命令的输出,使用率是98%

    0:027> !threadpool

    CPU utilization 98%

    Worker Thread: Total: 5 Running: 5 Idle: 0 MaxLimit: 200 MinLimit: 2

    Work Request in Queue: 4

    AsyncTimerCallbackCompletion TimerInfo@001c9990

    …..

    --------------------------------------

    Number of Timers: 7

    --------------------------------------

    Completion Port Thread:Total: 1 Free: 1 MaxFree: 4 CurrentLimit: 0 MaxLimit: 200 MinLimit: 2

    Q:根据进程的cpu使用率的关系,这里的cpu使用率是怎么显示的?

    A:这是一个有点矛盾的问题,cpu的使用率显示的是在该系统上这个进程使用了多少的cpu,或其他某个进程使用了多少cpu。通常的,在一个专用的web服务器上,你可以使用这个来作为一个参考来指示cpu占用率是不是很高,但最好的方法还是查看性能技术器的日志。

    Q:如果你运行tinyget,你可能会看到一些请求队列,例如:Work Request in Queue: 4,为什么请求会排成队列呢?为什么他们没有按计划的来执行?(和Running worker threads 比较,还有 worker threads 的最大限制)

    A:在这里例子中,我其实运行了tinyget 两次,我有5个工作线程(worker threads),多有的请求,尽管他们比最大工作线程要小很多(100 线程/1处理器,2处理器=200线程),当cpu使用率超过80%的时候没有新的线程被启动。所以在这以后的导致了100%的请求进来后会被放到等待队列中,等待新的线程被启动或其他线程空闲。

    查找是那些线程消耗了大部分的cpu资源

    1)运行 .time 来看看uptime 和进程的 cpu user time

    0:027> .time

    Debug session time: Fri Feb 22 12:26:55.000 2008 (GMT+1)

    System Uptime: 8 days 9:17:00.157

    Process Uptime: 0 days 0:01:07.000

      Kernel time: 0 days 0:00:06.000

      User time: 0 days 0:00:45.000

    2)  运行 !runaway 来看看所有线程的 usertime

    0:027> !runaway

     User Mode Time

      Thread       Time

      18:704       0 days 0:00:17.843

      19:9f4       0 days 0:00:13.328

      16:1948      0 days 0:00:10.718

      26:a7c       0 days 0:00:01.375

      24:114       0 days 0:00:01.093

      27:d54       0 days 0:00:00.390

      28:1b70      0 days 0:00:00.328

       0:b7c       0 days 0:00:00.171

      25:3f8       0 days 0:00:00.000

      …..

    Q:从输出中,看看是哪些线程消耗了大部分的cpu

    A:看看线程 18 19 16 号,他们总共消耗了大约42秒的时间

    Q:被进程消耗的总的cpu时间是多少?(从 .time 来看)

    A:从 .time 命令的输出看出,进城消耗了大约 1分钟 7秒的时间,45秒消耗在了用户模式下,这个时间是两个cpu的总的有效时间。

    注意,看看 !runaway 的输出是有点滑稽的,有下面几个理由:

    首先,在一个多cpu或多核的cpu的机器上,你必须记住,用户模式时间(usermode time,花在用户模式下的时钟周期)是所有处理器上的cpu的时间的总和,因此 把用户模式下的线程时间加起来的总和会比 .time 中看到的流逝的时间还要大。

    其次!runaway输出的是线程启动后就开始计时的用户模式时间。在一个asp.net环境中,一个线程会被多个请求拿来重用,所以在一个线程上一个很高的用户模式的时间并不意味着一定是运行于该线程上的请求导致了高cpu的问题。

    最后,在一些线程中,比如GC线程(在多处理器中,serverGC 进程)会在正在的进程周期中保持存在,所以它们有更高的可能性相对其他工作线程来说,占用了更多的用户模式时间。所以查看两个连续的dump文件,比较用户模式里面的线程的不同是更加有意义的。

    3)  选择一个有高cpu占用时间的线程,看看它的调用堆栈。

    ~#s                 (切换线程,请把#  换成 !runaway 中实际的线程号)

    kb 2000             (查看本地(原生)调用堆栈

    !clrstack             (查看 dotnet 调用堆栈)

    0:018> kb

    ChildEBP RetAddr  Args to Child             

    0290fd90 7d4d8f0e ffffffff 0290fde8 00000000 ntdll!ZwAllocateVirtualMemory+0x12

    0290fddc 7d4d8f51 ffffffff 0aec1000 0030d000 kernel32!VirtualAllocEx+0x41

    0290fdf8 79e7495b 0aec1000 0030d000 00080000 kernel32!VirtualAlloc+0x18

    0290fe38 79e74929 0aec1000 0030d000 00080000 mscorwks!EEVirtualAlloc+0x104

    0290fe50 79e7499b 7a3b45f0 0aec1000 0030d000

    mscorwks!CExecutionEngine::ClrVirtualAlloc+0x14

    0290fe6c 79fbdce8 0aec1000 0030d000 00080000 mscorwks!ClrVirtualAlloc+0x1a

    0290fe88 79fc2efa 0aec0b18 0030d6b0 0aec0b18 mscorwks!SVR::reset_memory+0x3e

    0290fea4 79fc2eb6 0aec0b18 0030d6b0 00000001

    mscorwks!SVR::gc_heap::make_unused_array+0x1b

    0290fec4 79fc3d1d 0aec0b18 0030d6b0 001a8970 mscorwks!SVR::gc_heap::thread_gap+0x30

    0290fef0 79fc3c15 00000000 001a8800 00000002

    mscorwks!SVR::gc_heap::sweep_large_objects+0xd4

    0290ff28 79efa9ba 00000002 00000000 001a8800

    mscorwks!SVR::gc_heap::mark_phase+0x3c5

    0290ff54 79efaf60 ffffffff 00000000 001a8800 mscorwks!SVR::gc_heap::gc1+0x46

    0290ff74 79efa72f 00000000 00000000 001a8800

    mscorwks!SVR::gc_heap::garbage_collect+0x246

    0290ff98 79fc8583 79fc8559 79f95b5c 79fc857c

    mscorwks!SVR::gc_heap::gc_thread_function+0x6a

    0290ffb8 7d4dfe21 001a8800 00000000 00000000

    mscorwks!SVR::gc_heap::gc_thread_stub+0x92

    0290ffec 00000000 79fc8538 001a8800 00000000 kernel32!BaseThreadStart+0x34

     

    0:016> kb

    ChildEBP RetAddr  Args to Child             

    0252f084 7d4d8c82 00000308 00000000 00000000 ntdll!ZwWaitForSingleObject+0x15

    0252f0f4 79e789c6 00000308 ffffffff 00000000 kernel32!WaitForSingleObjectEx+0xac

    0252f138 79e7898f 00000308 ffffffff 00000000 mscorwks!PEImage::LoadImage+0x1af

    0252f188 79e78944 ffffffff 00000000 00000000 mscorwks!CLREvent::WaitEx+0x117

    0252f19c 79fbc82a ffffffff 00000000 00000000 mscorwks!CLREvent::Wait+0x17

    0252f1b0 79fbc6da 00000000 58ec767e 00000000

    mscorwks!SVR::GCHeap::WaitUntilGCComplete+0x34

    0252f1ec 79fc2945 58ec75d2 00032cff 0000909f

    mscorwks!Thread::RareDisablePreemptiveGC+0x1a0

    0252f240 79fc2b07 00000000 0b376f34 7936d6e7

    mscorwks!Thread::RedirectedHandledJITCase+0x13d

    0252f2cc 66f12980 06f00f70 06f53d34 6628efd2

    mscorwks!Thread::RedirectedHandledJITCaseForGCThreadControl+0x7

    0252f2d8 6628efd2 06f00f70 02ef3cec 06f53d24

    System_Web_RegularExpressions_ni+0x12980

    0252f2ec 6613cb04 06f00f70 02ef3cec 02eee62c System_Web_ni+0x36efd2

    0252f300 6613cb50 0252f50c 02ef3cec 02ef3cec System_Web_ni+0x21cb04

    0252f4e8 6614c717 02ef11ac 00000000 02ee35fc System_Web_ni+0x21cb50

    0252f50c 6614d8c3 00000001 02eee62c 00000000 System_Web_ni+0x22c717

    0252f544 6614d80f 00000001 06eb01e4 02eb446c System_Web_ni+0x22d8c3

    0252f580 6614d72f 02ef3cec 6614d6c2 00000000 System_Web_ni+0x22d80f

    0252f5a0 65fe6bfb 660ee554 06f0a36c 02eee050 System_Web_ni+0x22d72f

    0252f5d4 65fe3f51 00000000 0000006e 00000000 System_Web_ni+0xc6bfb

    0252f610 65fe7733 0252f638 06f00110 02eee35c System_Web_ni+0xc3f51

    0252f664 65fccbfe 0252f6b4 02ef0f6c 06f00fe8 System_Web_ni+0xc7733

    Q:它们在干什么?你看这这些,能假设是什么导致了gaocpu么?

    A18  19  是两个GC 线程(gc_heap::gc_thread_stub),16号是运行托管代码的,但,当前正在等待GC完成(GCHeap::WaitUntilGCComplete),所以在这点上,16号是空闲的(idle),所以现在看来大部分的cpu是消耗在了GC线程上了。

    4)  这个仅适用在多处理器的机器上,因为他们有专用的GC线程,把 !runaway 中的用户模式下的GC 线程的时间加起来 除以 .time 中的用户模式的时间。

    Q:在用户模式的时间中,GC线程花费了多少的时间?

    A18 19  线程使用了超过21秒的cpu时间,=> 大约50%cpu总时间,那是在垃圾收集中花费了非常多的时间。

    检查性能监视器的日志

    1)  打开性能监视器,添加技术器:.NET CLR Memory/% Time in GC, # Gen 0 Collections, #Gen 1 Collections, #Gen 2 Collections  Large Object Heap Size

    Q% Time in GC 这个技术器是衡量什么指标的?

    A:这个计数器是:表示自从最后一个GC回收开始,花费在垃圾收集上的时间占所流逝的时间的百分比。这个计数器通常是表示应用程序回收合并内存过程中垃圾收集器所作工作的一个效能指示。在每个GC运行结束后,这个计数器会更新,计数器的值代表最后一次观察到的值,它不是平均值。换句话说,在Gen 2 收集之后,曲线肯定会有一个跃起,关键是这里,这个GC的时间比较平均起来没有在很高的地方结束。

    Q:在压力测试中,% Time in GC 的平均值是多少?

    A:在我这里大概是50%-- 60%,这个在很大程度上取决于你是单处理器还是多处理器,因为 server GC workstation GC 是根据这个不同而进行不同优化的。运行在多处理器上的asp.net 服务中的 Server GC 会做更多完全的收集,重新分配和合并内存块,workstation版的会在高内存消耗的时候省略掉一些做法。

    Q% Time in GC的合适的值是多少?

    A:这是一个很滑稽的问题,有些人说,有些说是30 ,但我觉得在大多数的asp.net应用中5%是很难达到的,特别是这种高内存使用量的来说。然而,基于经验,我认为,如果在一个进程中30%cpu使用被用来做垃圾收集,那是多么愚蠢的。虽然这样说,但是低的% Time in G也是非常狡猾的,因为它会改变应用的内存分配方式的改变。所以说任何的优化,尽量做到把它调到合适的水平,不要做的过度。

    Q:在Gen 0, Gen 1  Gen 2 collections 之间的比率是多少?什么样的情况是可以接受的? 为什么?

    AGen 0 的收集基本上是不需要消耗什么的,免费的。Gen 1 的收集(包含Gen 0 的收集)也是非常廉价的,因为我们仍然使用着很少数量的要分配的或回收的内存等。Gen 2 的收集从另一方面来说将是非常非常地昂贵的,因为它处理的是整个dotnet GC 堆。最理想的比率是 100Gen0 的收集,10次的Gen 1 收集,1次的Gen 2 的收集,即:10010,在这里例子中是

                                 5853 Gen 0 Collections

    5830 Gen 1 Collections

    5430 Gen 2 Collections

    这些值说明了,在非常多次的每一次的收集行为中,Gen 2  发生了很多次(因为这个页触发 Gen 0 Gen 1 的收集)。这是非常坏的,因为我们并没有使用到GC给我们带来的好处,这不是一个一般意义上的GC,而是它总是不停的要寻找整个内存,释放回收……。

    Q:是什么东西会导致象上面这样的比率出现?

    A:通常情况下,两种事情导致这样的比率。一个是非常频繁的分配大对象,以致于每次LOH的段都用完了,二是你经常的直接调用GC.Collect方法,或者GC.GetTotalMemory(true) 这样的方法。如果有人手动的调用了这些方法,那.NET CLR Memory/# Induced GC 这个计数器就会上升,但在这里,你可以看见它保持平稳,这就是说,我们可能我们在一直不停的申请大对象。

    查看dump文件,找出在GC中是什么导致了高cpu

    1)运行 ~* kb 2000 得到所有的本地调用堆栈,寻找线程中触发GC的函数(mscorwks!SVR::GCHeap::GarbageCollectGeneration

     

      27  Id: bcc.d54 Suspend: 1 Teb: fff06000 Unfrozen

    ChildEBP RetAddr  Args to Child             

    103eed84 7d4d8c82 00000314 00000000 00000000 ntdll!ZwWaitForSingleObject+0x15

    103eedf4 79e789c6 00000314 ffffffff 00000000 kernel32!WaitForSingleObjectEx+0xac

    103eee38 79e7898f 00000314 ffffffff 00000000 mscorwks!PEImage::LoadImage+0x1af

    103eee88 79e78944 ffffffff 00000000 00000000 mscorwks!CLREvent::WaitEx+0x117

    103eee9c 79efafc5 ffffffff 00000000 00000000 mscorwks!CLREvent::Wait+0x17

    103eeec0 79efad3d ffffffff 001ab0e0 001ab188 mscorwks!SVR::gc_heap::wait_for_gc_done+0x62

    103eeee8 79efa339 00000000 00000000 001ab0e0 mscorwks!SVR::GCHeap::GarbageCollectGeneration+0x1b5

    103eef78 79ef98cf 103eefb4 00047470 00000003 mscorwks!SVR::gc_heap::try_allocate_more_space+0x136

    103eef94 79f20ee4 103eefb4 00047470 00000003 mscorwks!SVR::gc_heap::allocate_more_space+0x2e

    103eefd8 79f3d20e 00047470 00000000 00000000 mscorwks!SVR::gc_heap::allocate_large_object+0x5a

    103eeff4 79e7510e 0f468840 00047470 00000000 mscorwks!SVR::GCHeap::Alloc+0x8d

    103ef010 79e86713 00047470 00000000 00000000 mscorwks!Alloc+0x60

    103ef04c 79e865b9 00023a30 4a807762 0773cd58 mscorwks!SlowAllocateString+0x29

    103ef0f0 79378b7d 0773cd58 00023a2f 00000008 mscorwks!FramedAllocateString+0xa0

    103ef104 79378af4 103ef14c 0d36b268 0773cd1c mscorlib_ni+0x2b8b7d

    103ef12c 7a4a88d6 00000000 031df118 031deef0 mscorlib_ni+0x2b8af4

    103ef14c 66f12980 06f00098 031deaec 6628efd2 System_ni+0x688d6

    103ef38c 6614d8c3 00000001 03198328 00000000 System_Web_RegularExpressions_ni+0x12980

    103ef3c4 6614d80f 00000001 06eb01e4 02eb446c System_Web_ni+0x22d8c3

    103ef400 6614d72f 031ddfd8 6614d6c2 00000000 System_Web_ni+0x22d80f

     

    0:016> !clrstack

    OS Thread Id: 0x1948 (16)

    ESP       EIP    

    0252f208 7d61c828 [RedirectedThreadFrame: 0252f208]

    0252f24c 79363058 System.String.wstrcpy(Char*, Char*, Int32)

    0252f258 7936d6e7 System.String.FillStringChecked(System.String, Int32, System.String)

    0252f278 79378b9f System.String.ConcatArray(System.String[], Int32)

    0252f28c 79378af4 System.String.Concat(System.Object[])

    0252f2a0 0fe90a2a AllProducts.Page_Load(System.Object, System.EventArgs)

    0252f2d8 66f12980 System.Web.Util.CalliHelper.EventArgFunctionCaller(IntPtr, System.Object, System.Object, System.EventArgs)

    0252f2e8 6628efd2 System.Web.Util.CalliEventHandlerDelegateProxy.Callback(System.Object, System.EventArgs)

    0252f2f8 6613cb04 System.Web.UI.Control.OnLoad(System.EventArgs)

    0252f308 6613cb50 System.Web.UI.Control.LoadRecursive()

    0252f31c 6614e12d System.Web.UI.Page.ProcessRequestMain(Boolean, Boolean)

    0252f518 6614d8c3 System.Web.UI.Page.ProcessRequest(Boolean, Boolean)

    0252f550 6614d80f System.Web.UI.Page.ProcessRequest()

    0252f588 6614d72f System.Web.UI.Page.ProcessRequestWithNoAssert(System.Web.HttpContext)

    0252f590 6614d6c2 System.Web.UI.Page.ProcessRequest(System.Web.HttpContext)

    0252f5a4 0fe90205 ASP.allproducts_aspx.ProcessRequest(System.Web.HttpContext)

    0252f5a8 65fe6bfb System.Web.HttpApplication+CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()

    0252f5dc 65fe3f51 System.Web.HttpApplication.ExecuteStep(IExecutionStep, Boolean ByRef)

    0252f61c 65fe7733 System.Web.HttpApplication+ApplicationStepManager.ResumeSteps(System.Exception)

    0252f66c 65fccbfe System.Web.HttpApplication.System.Web.IHttpAsyncHandler.BeginProcessRequest(System.Web.HttpContext, System.AsyncCallback, System.Object)

    0252f688 65fd19c5 System.Web.HttpRuntime.ProcessRequestInternal(System.Web.HttpWorkerRequest)

    0252f6bc 65fd16b2 System.Web.HttpRuntime.ProcessRequestNoDemand(System.Web.HttpWorkerRequest)

    0252f6c8 65fcfa6d System.Web.Hosting.ISAPIRuntime.ProcessRequest(IntPtr, Int32)

    0252f8d8 79f047fd [ContextTransitionFrame: 0252f8d8]

    0252f90c 79f047fd [GCFrame: 0252f90c]

    0252fa68 79f047fd [ComMethodFrame: 0252fa68]

    Q:为什么GC 会被触发?

    A:我们尝试去分配一个大对象(gc_heap::allocate_large_object),为了达到目的,我们需要更多的空间,那就触发了垃圾收集。那个要分配的对象大小是0x47470 = 291952 bytes,那就是位于LOH结尾处的东西。

    Q:它正在分配的是什么样类型的对象?

    A:基于托管栈,它看起来象是一个 char[] 或是一个string,因为我们包含strings,它在内部做wstrcpy

    Q:线程正在干什么?

    A:它正在处理一个AllProducts.aspx页面的请求,调用了Page_Load 函数,里面有字符串连接。

    Q:是这个导致了 Gen 0 Gen1 Gen2 的比率不正常么?为什么?

    A:包含大的字符串会导致高的cpu,这是众所周知的。关于原因,我会再写一篇文章。这里不深入讨论。如果你在循环中做 str=str1+str2,你会创建一个新的对象,而不是把原来的对象扩大,如果不停的循环,strings会变大,你就不要不停的创建新的strings,那么一个就比一个大,这就导致LOH段被频繁的扩大,最终结果导致很多GC的发生和高cpu

    2)找出LOH上面有什么东西,如果你不幸,你抓到的GG正在计划或重新分配(plan or relocate)阶段,那 !dumpheap 的输出就会步正确。如果那样的话,你要!dumpheap -min 85000

    0:016> !dumpheap -min 85000

    The garbage collector data structures are not in a valid state for traversal.

    It is either in the "plan phase," where objects are being moved around, or

    we are at the initialization or shutdown of the gc heap. Commands related to

    displaying, finding or traversing objects as well as gc heap segments may not

    work properly. !dumpheap and !verifyheap may incorrectly complain of heap

    consistency errors.

    ------------------------------

    Heap 0

     Address       MT     Size

    03481a14 001a86d8   136876 Free

    0aec0b18 001a86d8  3200688 Free

    0b1ce1c8 790fd8c4   246832    

    0b20a608 790fd8c4   246992    

    0b246ad8 790fd8c4   462032    

    0b2b77a8 001a86d8   784256 Free

    0b376f28 790fd8c4   416272    

    0b3dc938 001a86d8   336960 Free

    0b42ed78 7912dae8  1048592    

    0b52ed88 790fd8c4   416432    

    total 10 objects

    ------------------------------

    Heap 1

     Address       MT     Size

    object 06eb0038: does not have valid MT

    curr_object : 06eb0038

    ----------------

    0ceb0048 001a86d8  1180896 Free

    0cfd0528 790fd8c4   590672    

    0d060878 001a86d8  3189232 Free

    0d36b268 790fd8c4   291792    

    total 4 objects

    ------------------------------

    total 14 objects

    Statistics:

          MT    Count    TotalSize Class Name

    7912dae8        1      1048592 System.Byte[]

    790fd8c4        7      2671024 System.String

    001a86d8        6      8828908      Free

    Total 14 objects

    QLOH上面有什么?

    A:在这里,因为我们在GC的里面,所以我们不能真的横贯GG 堆,这就是说 这个命令输出中没有把所有的大对象显示给我们,但根据我们的理论,始终如一的主要的是Byte[] Strings ,它们包含字符串连接。

    Q:在字符串的大小里面是不是有什么规律?

    A:仅仅在这里可以做为一个证据,并不是在所有的案例中都可以。这里有一对stirngs string246832 bytes and 246992 bytes)和另外一对strings stings 416272 and 416432 bytes),它们的大小相差160Bytes。如果你恰好得到一个dump文件,堆已经停止运转,你会看到一个很长的strings 的列(list)。这些strings大家不断的以一定的数字增加,这个基本上是因为每个都不断地和那个数字长度的string连接。

    2)把一些strings 输出来看看内容

    0:016> !do 0b1ce1c8

    Name: System.String

    MethodTable: 790fd8c4

    EEClass: 790fd824

    Size: 246832(0x3c430) bytes

     (C:WINDOWSassemblyGAC_32mscorlib2.0.0.0__b77a5c561934e089mscorlib.dll)

    String:

    <table><tr><td><B>Product ID</B></td><td><B>Product Name</B></td><td><B>Description</B></td></tr><tr><td>0</td><td>Product 0</td><td>Description for Product 0</td>

    </tr><tr><td>1</td><td>Product 1</td><td>Description for Product 1</td></tr><tr><td>2</td><td>Product 2</td><td>Description for Product 2</td></tr><tr><td>3</td>

    <td>Product 3</td><td>Description for Product 3</td></tr><tr><td>4</td><td>Product 4</td><td>Description for Product ...

    Q:基于栈,垃圾收集的知识,和字符串的内容,你能不能找到哪里的代码有问题?

    A:前面提到过是Page_load 的里面有字符串相连接。导致了高cpu。这个代码看起来像是组织一个产品的表格。

    检查代码,证实你的假设

    1)  打开AllProducts.aspx.cs 看看代码

     protected void Page_Load(object sender, EventArgs e)

        {

            DataTable dt = ((DataLayer)Application["DataLayer"]).GetAllProducts();

            string ProductsTable = "<table><tr><td><B>Product ID</B></td><td><B>Product Name</B></td><td><B>Description</B></td></tr>";

     

            foreach (DataRow dr in dt.Rows)

            {

                ProductsTable += "<tr><td>" + dr[0] + "</td><td>" + dr[1] + "</td><td>" + dr[2] + "</td></tr>" ;

            }

            ProductsTable += "</table>";

            tblProducts.Text = ProductsTable;

        }

     Q:怎么解决?为什么这样能解决问题?

     A:使用StringBuilder 来附加数据,而不是使用字符串连接。因为string builder 有一个缓冲区,它可以放字符串,不用每次都创建新的对象,当它需要扩展缓冲区的时候,它会加倍的申请,随着100 1000的因子不同,而预分配缓冲区,所以应用程序需要在LOH分配新的内存的次数就会降低。如果你使用了stringBuilder你会发现cpu使用率就降下来了,CC调用的次数也会降下来,你会发现垃圾收集的次数就不是5000多次而是少于10次完全的收集。

     
     
     
     
     

    https://www.cnblogs.com/softfair/archive/2008/03/03/dotnet-Debugging-Demos---Lab-4-HighCPU--Review.html

  • 相关阅读:
    软件工程第一次作业
    20145101《JAVA程序设计》课程总结
    20145101《Java程序设计》第10周学习总结
    20145101《Java程序设计》第9周学习总结
    20145101《Java程序设计》第8周学习总结
    20145101 《Java程序设计》第7周学习总结
    20145101 第二次实验报告
    20145101实验一 实验报告
    20145101《Java程序设计》第6周学习总结
    20145101《Java程序设计》第5周学习总结
  • 原文地址:https://www.cnblogs.com/micro-chen/p/14822519.html
Copyright © 2011-2022 走看看