Yet another Death by the ViewState
Yesterday I ran into an issue where the users were complaining about the application being very slow in a specific page.
If you read the title you will probably think “here is another post around how viewstate can cause all sort of problems in my application if it´s not well used bla bla bla …”, but keep with me for a couple more minutes.
If you look at search engines you will find a lot of posts around this issue, but since I keep seeing the same mistakes being made over and over (sometimes, developers I talk with don´t even realize they are making this mistakes) so I though well one more post around this won´t hurt, if at least one person realizes it was helpful (well, I will be happy).
My goal here is not just to show you how to look at the memory dump on troubleshooting viewstate issues (there are a lot of excellent posts around this issue already) but to make you thinking what viewstate is all about. And for that I will end this post with a small pop quiz (and there is the important message I want to pass on)
The first step was to take some memory dumps while the issue was happening. Then let´s see requests executing. For this you can use the sos command !aspxpages. My output was
0:000> !aspxpages
0x0aab6b2c 300 Sec no 13 Sec 23 200 POST /Subsite/page.aspx
Ok, I see a request that’s going on for about 13 seconds (I’ve opened the subsequent memory dumps I took and saw the same request and the same stack)
So let´s look at what this thread was doing
0:000> ~23s Switch to the right thread
0:023> !clrstack Show managed stack
Loaded Son of Strike data table version 5 from "C:\WINDOWS\Microsoft.NET\Framework\v1.1.4322\mscorwks.dll"
Thread 23
ESP EIP
08a7f2b4 7c82ed54 [FRAME: HelperMethodFrame]
08a7f2ec 087ba3b2 [DEFAULT] [hasThis] String System.Web.UI.LosFormatter.ConsumeOneToken()
08a7f300 087ba167 [DEFAULT] [hasThis] Object System.Web.UI.LosFormatter.DeserializeValueInternal()
08a7f334 087ba077 [DEFAULT] [hasThis] Object System.Web.UI.LosFormatter.DeserializeValueInternal()
08a7f368 087b9efc [DEFAULT] [hasThis] Object System.Web.UI.LosFormatter.DeserializeValueInternal()
08a7f39c 087b9ece [DEFAULT] [hasThis] Object System.Web.UI.LosFormatter.DeserializeValueInternal()
08a7f3d0 087b9f41 [DEFAULT] [hasThis] Object System.Web.UI.LosFormatter.DeserializeValueInternal()
08a7f404 087ba077 [DEFAULT] [hasThis] Object System.Web.UI.LosFormatter.DeserializeValueInternal()
08a7f438 087b9f9d [DEFAULT] [hasThis] Object System.Web.UI.LosFormatter.DeserializeValueInternal()
08a7f46c 087ba077 [DEFAULT] [hasThis] Object System.Web.UI.LosFormatter.DeserializeValueInternal()
08a7f4a0 087b9f9d [DEFAULT] [hasThis] Object System.Web.UI.LosFormatter.DeserializeValueInternal()
08a7f4d4 087ba077 [DEFAULT] [hasThis] Object System.Web.UI.LosFormatter.DeserializeValueInternal()
08a7f508 087b9f9d [DEFAULT] [hasThis] Object System.Web.UI.LosFormatter.DeserializeValueInternal()
08a7f53c 087b9f6f [DEFAULT] [hasThis] Object System.Web.UI.LosFormatter.DeserializeValueInternal()
08a7f570 087b6729 [DEFAULT] [hasThis] Object System.Web.UI.LosFormatter.Deserialize(String)
08a7f5a8 087b60c3 [DEFAULT] [hasThis] Object System.Web.UI.Page.LoadPageStateFromPersistenceMedium()
08a7f5e0 087b5f23 [DEFAULT] [hasThis] Void System.Web.UI.Page.LoadPageViewState()
08a7f8a0 9e8a4104 [FRAME: ContextTransitionFrame]
0:023> knL Native stack
# ChildEBP RetAddr
00 08a7ec80 7c822114 ntdll!KiFastSystemCallRet
01 08a7ec84 77e67143 ntdll!ZwWaitForMultipleObjects+0xc
02 08a7ed2c 77e6109d kernel32!WaitForMultipleObjectsEx+0x11a
03 08a7ed48 79248be3 kernel32!WaitForMultipleObjects+0x18
04 08a7ef80 791e0723 mscorwks!Thread::SysSuspendForGC+0x248
05 08a7ef98 792144f9 mscorwks!GCHeap::SuspendEE+0xcf
06 08a7efb4 7924199e mscorwks!GCHeap::GarbageCollectGeneration+0x103
07 08a7efe4 791bedfd mscorwks!gc_heap::allocate_more_space+0x13a
08 08a7f014 792d341e mscorwks!gc_heap::allocate_large_object+0x8e
09 08a7f234 791b3013 mscorwks!GCHeap::Alloc+0x12a
0a 08a7f244 791dd118 mscorwks!Alloc+0x3a
0b 08a7f264 791dd0b9 mscorwks!FastAllocatePrimitiveArray+0x45
Hummm, let me see what this viewstate is all about. Executing !dso on this thread to see what objects were on stack
0:023> !dso Shows stack objects on this thread
Thread 23
(…)
0x8a7f540 0xab0b7b0 System.Web.UI.LosFormatter
0x8a7f54c 0x2bfedd8 System.Web.CharBufferAllocator
0x8a7f554 0xab0b7b0 System.Web.UI.LosFormatter
0x8a7f57c 0xbf637e8 System.String dDw1MzgxO3Q8O2w8aTwzPjs+O2w8dDw7bDxpPDA+
(…)
Let me see the size of this string that looks like viewstate J
0:023> !objsize 0xbf637e8 Show the size of the object
sizeof(0bf637e8) = 942124 ( 0xe602c) bytes (System.String)
Now that is pretty big. If you really want to see decode this viewstate I suggest you to use ViewStateDecoder made by Fitz Onion. Also on Tess blog you will find a lot of information around viewstate issues. One such example is https://blogs.msdn.com/tess/archive/2008/09/09/asp-net-memory-identifying-pages-with-high-viewstate.aspx
I´ve also set some performance counters around GC collections the ration between GEN 1 and GEN 2 was around 1:2
Now here comes the part that I really, really want you think about because I believe this will break some conceptions usually people have about viewstate (and hopefully you will search on this topic and review some of the concepts around viewstate). There are a lot of excellent posts on this matter
POP QUIZ
Imagine you have a dropdownbox with viewstate disabled that is filled with a list of countries from a database on the OnLoad event
myList.DataSource = GetListOfCountries();
myList.DataBind();
You select a country and click on some button. The page will postback and you will see that your selection is not retained.
My question is why isn´t the selected value retained? I will also leave a hint. It has nothing to do with viewstate being disabled.
Have fun
Bruno