Shallow Copy Vs Deep Copy
In short: A shallow copy duplicates only the top-level fields of an object. If the object holds a handle to another object, the copy shares that same inner object, so changing it through one handle affects both. A deep copy duplicates the top-level fields and also builds fresh copies of every nested object, so the two objects are fully independent. In SystemVerilog, b2 = new b1; gives a shallow copy; a deep copy needs a custom copy() method that news each inner object.
Classes in SystemVerilog hold data, and that data can include handles to other objects. When you copy an object you have to decide how far the copy goes. A shallow copy stops at the first level: value fields are duplicated, but any handle field still points at the same nested object. A deep copy goes all the way down, creating new copies of the nested objects too. Getting this wrong is a classic source of testbench bugs where two transactions accidentally share the same payload.
A simple way to picture it
Imagine you photocopy a folder that contains a sticky note pointing to a shared filing cabinet. A shallow copy gives you a new folder, but the sticky note in both folders still points to the same one cabinet. Scribble in that cabinet and both folders “see” the change. A deep copy also builds you a brand-new cabinet with its own copies of the papers, so now each folder has its own. Edit one and the other is untouched.
Value fields vs handle fields
The whole topic comes down to one distinction. A value field (like an int) is stored directly, so copying it makes a truly separate value. A handle field (a class variable) stores only a pointer to an object, so copying the handle just gives you a second pointer to the same object. Shallow copy duplicates the handle; deep copy duplicates the object it points to.
Shallow copy: a worked example
Here packet holds an int value and a handle to a payload object. The shallow copy with new p1 duplicates the value but shares the payload.
class payload;
int data = 15;
endclass
class packet;
int id = 10; // value field
payload pl = new; // handle field (points to a payload)
endclass
module tb;
initial begin
packet p1 = new;
packet p2 = new p1; // shallow copy of p1 into p2
p2.id = 20; // changes p2 only (value field)
p2.pl.data = 30; // changes the SHARED payload
$display("p1.id=%0d p2.id=%0d", p1.id, p2.id);
$display("p1.pl.data=%0d p2.pl.data=%0d", p1.pl.data, p2.pl.data);
end
endmoduleBecause id is a value field, p1.id stays 10 while p2.id becomes 20. But pl is a shared handle, so writing p2.pl.data also changes p1.pl.data. Both read back as 30. That shared inner object is the trap of shallow copy.
Deep copy: a worked example
To make the two packets fully independent, add a copy() method that news a fresh payload and copies its contents.
class payload;
int data = 15;
function payload clone(); // helper to duplicate this payload
clone = new;
clone.data = this.data;
endfunction
endclass
class packet;
int id = 10;
payload pl = new;
function void copy(packet src); // custom deep copy
this.id = src.id;
this.pl = src.pl.clone(); // new payload, not shared
endfunction
endclass
module tb;
initial begin
packet p1 = new;
packet p2 = new;
p2.copy(p1); // deep copy
p2.id = 20;
p2.pl.data = 30; // changes p2 ONLY now
$display("p1.pl.data=%0d p2.pl.data=%0d", p1.pl.data, p2.pl.data);
end
endmoduleNow the payloads are separate objects. Writing p2.pl.data = 30 leaves p1.pl.data at its original 15. The copy() method also gives you control: you can choose which fields to copy and which to leave out.
Side by side comparison
| Aspect | Shallow copy | Deep copy |
|---|---|---|
| Value fields | Duplicated (independent) | Duplicated (independent) |
| Handle fields | Handle shared (same inner object) | Inner object duplicated (independent) |
| How to do it | b2 = new b1; | Custom copy() that news each nested object |
| Independence | Partial: inner objects are shared | Full: nothing is shared |
| Cost | Cheap and quick | More code and more memory |
| Risk | Accidental sharing bugs | Very little, if written carefully |
Which one should you use?
- Use a shallow copy when the class has only value fields, or when you actually want the copies to share the same inner object.
- Use a deep copy when the class holds handles and you need the copies to be independent, which is the common case for transactions in a testbench.
- In UVM, prefer the built-in
clone()andcopy()paths, which perform a deep copy of the fields you register with the field macros or implement in do_copy.
Expected output, in plain words
In the shallow example, both packets report a payload data of 30 because they share one payload, while the id values differ (10 and 20). In the deep example, p1 keeps its payload data of 15 while p2 shows 30, because each now owns a separate payload. I have checked this against the language rules rather than run it, so confirm the exact prints in your own simulator or on EDA Playground.
Common mistakes to avoid
- Assuming
new b1copies everything. It copies only the first level; nested objects stay shared. - Sharing a transaction by accident. Pushing the same handle into a queue many times stores one object many times, not many copies. Deep copy or create fresh items.
- Forgetting to copy nested objects in do_copy. In UVM, override do_copy or register nested fields so clone() truly goes deep.
- Deep copying when you do not need to. If a class has only value fields, a shallow copy is enough and cheaper.
- Copying a null handle. Check that a nested handle is non-null before you call new on it in your copy method.
Keep learning
Copying builds on class basics. See encapsulation in SystemVerilog for how to protect the fields you copy, and inheritance in SystemVerilog for how copy methods extend down a class tree. For how transactions move through a testbench, read the UVM testbench architecture. More is in our SystemVerilog guides and interview questions.
Frequently asked questions
What is the difference between shallow copy and deep copy?
A shallow copy duplicates the top-level fields but shares any nested objects through their handles. A deep copy duplicates the top-level fields and also creates fresh copies of the nested objects, so the two objects are fully independent.
How do I make a shallow copy in SystemVerilog?
Use the copy constructor form b2 = new b1. This creates a new object and copies each field of b1 into it. Value fields become independent, but handle fields still point to the same nested objects that b1 uses.
How do I make a deep copy in SystemVerilog?
Write a custom copy method that copies value fields directly and calls new on each nested object so it gets its own copy. In UVM you usually rely on clone() and do_copy or the field macros to perform the deep copy.
Why does changing one object also change the other after a shallow copy?
Because a shallow copy shares handle fields. Both objects hold a handle to the same nested object, so a write through either handle changes the one shared object that both can see.
When is a shallow copy good enough?
A shallow copy is enough when the class contains only value fields, or when you intentionally want the copies to share the same nested object. It is cheaper and needs no custom method.
How does UVM handle copying transactions?
UVM provides clone() and copy(). If you register fields with the uvm_field macros or implement do_copy, these methods perform a deep copy of those fields, giving you independent transaction objects.

There is a typo mistake in below mentioned paragraph.
Actually class B instantiates another Class i.e. A. It means Class B is having two variables inside i.e. integer i and class variable a of type Class “A”.
Plz update it in below:
Here we are having two Classes named “Class A” & “Class B“. Class A instantiates another Class i.e. B. It means, Class A is having two variables inside i.e. integer variable i and Class variable a of type Class “A”