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 http://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

Comments (1)

  1. Col says:

    I presume that the dropdown is being repopulated as you are not doing a check on whether it is a postback (e.g. !Page.IsPostback) before populating the dropdown hence overwritting your selection