Pass By Value & Pass By Reference in Apex

In Apex, Primitive data types are passed as Values and Non-Primitive data types are passed as Reference. Every programming language is different. Since Apex which is bounded by Governor Limits, has its own memory limitations. Creating a repetitive copy of the variable here and there during each transaction will increase the Heap memory limit of 6MB.

Creating a copy of entire Non-Primitive Data Types like SObjects, Lists, Sets, Maps etc that store large amount of data is not feasible. Hence, Salesforce creates a fixed location to store complex data types in the Heap.

Pass By Value

When primitive data types like String or Integer are passed to the method, the method creates a copy of the value in its memory.

Any changes made to the value will reflect only inside that method, and the member variable whose data is passed to the method will not get affected whether the called method returns or not.

In short, in Apex when Primitive values are passed, the pointer (reference) is not present.

Pass By Reference

Non-primitive data types such as SObjects are passed by reference. This means the method will not create a copy, and it will refer to the same location from where that variable was created.

Any changes made to the value inside that method, the member variable whose data was passed to the method, will also reflect the same changes whether the called method returns or not.

In short, in Apex when Non-Primitive values are passed, the pointer still exists which point to the same location where the Data Type was initialized.

The only way to remove that Pointer is by creating a Deep Copy of the SObject Record. This can be done by using the clone() method of the SObject class, as shown in the example below. Learn More about Clone() Method.

The below example demonstrates the difference between passing by value and passing by reference.

public class PassByValueReference {
    public static Integer i = 1;
    public static Account acc = new Account(Name='Bharat');
    
    public static void run() {
        
        System.debug(runInt(i)); // 11
        System.debug(i); // 1
        
        runInt2(i);
        System.debug(i); // 1
        
        System.debug(runAcc(acc)); // India
        System.debug(acc); // India
        
        runAcc2(acc);
        System.debug(acc); // USA
        
        System.debug(runAccCopy(acc)); // Canada
        System.debug(acc); // USA
    }
    
    // Method Returns
    public static Integer runInt(Integer a) {
        System.debug('runInt a: '+a); // 1
        a = 11;
        return a;
    }
    
    // Method Doesnot Return
    public static void runInt2(Integer a) {
        System.debug('runInt a: '+a); // 1
        a = 12;
    }
    
    // Passing SObject (Method Returns)
    public static Account runAcc(Account a) {
        System.debug('runAcc a: '+a); // Bharat
        a.Name = 'India';
        return a;
    }
    
    // Passing SObject (Method Doesnot Return)
    public static void runAcc2(Account a) {
        System.debug('runAcc2 a: '+a); // India
        a.Name = 'USA';
    }
    
    // Deep Copy of SObject Record
    public static Account runAccCopy(Account a) {
        System.debug('runAcc2 a: '+a); // USA
        Account acc = a.clone();
        System.debug('runAcc2 acc: '+acc); // USA
        acc.Name = 'Canada';
        return acc;
    }
}

Note

The same concept applies to JavaScript as well. Objects and Arrays in JS are passed by reference.

In Apex, Java, JavaScript, unused memory pointers are automatically removed by the garbage collector. When the transaction completes, the variables will be reset.