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.