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