4

BACKGROUND

I want to build a wizard using Visualforce Components. For this I represent each step as a separate component. Each component has an attribute that represents a controller instance from the containing page:

<apex:attribute name="mainController" type="ContainerController" assignTo="{! controller }" required="true" 
                    description="This is the instance of the main controller." />

I then use the controller instance in components' controllers for shared properties, methods etc.

CODE (VF and APEX)

Container.vfp

<apex:page controller="ContainerController" sidebar="false" showHeader="false">
    <apex:form>
        <apex:outputPanel layout="block">
            <apex:commandButton value="Switch" action="{! switch }"/>
        </apex:outputPanel>

        <c:elementOne mainController="{! instance }" rendered="{! showOne }"/>
        <c:elementTwo mainController="{! instance }" rendered="{! showTwo }"/>
    </apex:form>
</apex:page>

ContainerController.apxc

public class ContainerController
{
    public Boolean showOne { get; set; }
    public Boolean showTwo { get; set; }
    public String[] mock { get; set; }

    public ContainerController()
    {
        showOne = true;
        showTwo = false;
        mock = new List<String>();
        for (Integer i=0; i<1000; i++)
            mock.add('foo-bar'+i);
    }

    public ContainerController getInstance() { return this; }    

    public void switch()
    {
        showOne = !showOne;
        showTwo = !showTwo;
    }
}

ElementOne.vfc

<apex:component controller="ElementOneController" allowDML="true">
    <apex:attribute name="mainController" type="ContainerController" assignTo="{! controller }" required="true" 
                    description="This is the instance of the main controller." />

    <apex:sectionHeader title="Element One" />
</apex:component>

ElementOneController.apxc

public class ElementOneController
{
    public ContainerController controller { get; set; }

    public ElementOneController(){}
}

ElementTwo.vfc

<apex:component controller="ElementTwoController" allowDML="true">
    <apex:attribute name="mainController" type="ContainerController" assignTo="{! controller }" required="true" 
                    description="This is the instance of the main controller." />

    <apex:sectionHeader title="Element Two" />
</apex:component>

ElementTwoController.apxc

public class ElementTwoController
{
    public ContainerController controller { get; set; }

    public ElementTwoController(){}
}

QUESTION

I enabled the Development Mode for my user and opened the /apex/Container page. When I checked the view state I was surprised to discover that the instance of ContainerController was duplicated.

enter image description here

Then I pressed the Switch button on the page, and the size tripled the original 2.75 KB and become 8.31 KB enter image description here

While the total size is 5.06 KB it seems that the size for the instance of ContainerController was counted once in the total calculation. That's not neat, but at least the total size is correct. That's what I thought...

Nay, when I checked my real real project, the view state calculation was "correct" - it duplicated the size of the same instance several times.

enter image description here

In my understanding when an instance of a object is passed as a parameter in Visualforce Component it's passed by reference which means that only one instance of that object exist. So View State should not contain duplicates of that object.

Does anybody know how an object instance used by apex:attribute is reflected in ViewState?

2
  • 1
    an easy test would be to declare an instance variable in the main controller as String stuff = 'a'.repeat(100000); and see if true viewstate blows up as you move from one component to the next
    – cropredy
    Jan 30, 2019 at 18:42
  • @cropredy, thanks for your advice. I played with 'a'.repeat(100000); and posted my findings as an answer.
    – Eduard
    Feb 5, 2019 at 10:48

2 Answers 2

1

There's two things going on here.

String Pools

Like Java, there is a String Pool. What this means is that there's a table in memory that stores each String used, so they occupy the same space. Each string is immutable, meaning that you can't directly manipulate a string, only create new strings. This improves memory/heap usage as as well as view state size. This is directly tied to using references, since strings are always "referenced."

References

References are when we refer to the same object more than once. A reference requires only 4 bytes of heap instead of the full cost of the object. The view state doesn't reflect it this way; thew view state is showing the full size of the object, but that's not how much space is actually being occupied. The top number shown for all components combined is correct.

0

It seems that the true View State is not impacted and object instances seems to "be passed by reference" in it. Below are the results of my experiment. For each test I used the same record. I started each test from the beginning and clicked through the all steps to the end. The screenshots below were taken at the last step.

View State before test:

enter image description here

View State with a single stuff instance variable:

String stuff = 'a'.repeat(1000000);

enter image description here

As you can see the stuff variable takes 1.13 KB in the View State. The instance of the main controller is shared by 3 controllers, so I expected the final size to be 17.9 + (1.13 * 3) + 1.13 = 22.42KB which appeared to be not.

The actual difference, though, between the View State before and after test is 2.69KB (20.59 - 17.9) which is only twice of the size of stuff. Why it's not 1.13KB is a mystery for me. My guess Salesforce adds some overhead into the view state for keeping instance references.

The same held true when I tested with multiple stuff instance variables:

String stuff1 = 'a'.repeat(1000000);
String stuff2 = 'a'.repeat(1000000);
String stuff3 = 'a'.repeat(1000000);
String stuff4 = 'a'.repeat(1000000);
String stuff5 = 'a'.repeat(1000000);

enter image description here

The total size for the view state increased by 7.44KB (25.34 - 17.9), which is a bit over the expected 5.65KB (5 stuffs * 1.13KB). If the view state contained duplicate data, we'd had seen the 22.6KB ((5.65KB * 3) + 5.65KB) increase in the total size.

SUMMARY

I'll leave this answer here until someone proposes a better explanation. It seems that View State calculation is complex and it can show the intermediary results, like total size for controllers, bigger than the original size. This becomes confusing when a developer tries to estimate efficiency of his/her solution by analyzing the view state.

3
  • this is sort of unexpected - stuff should be 1 MB per the way you instantiated it and viewstate should blow up
    – cropredy
    Feb 5, 2019 at 14:31
  • Yes, I even used 1,000,000 instead of 100,000, and stuff was 1.13KB. For 100,000 it was even smaller.
    – Eduard
    Feb 5, 2019 at 15:14
  • this suggests at some form of compression - change stuff to be a list of sobjects that you fetch once at start of wizard to be, say, several tens to hundreds of some object that you have lots of in your sandbox (users? pricebookentries? leads?)
    – cropredy
    Feb 5, 2019 at 18:18

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .