Let’s go step by step and see how PHP Garbage Collection & Memory Management behave when using classes, methods, and heavy tasks like PDF/Excel generation.
🏗️ 1. Classes & Methods
When you use classes and objects in PHP:
<?php
class User {
public $name;
function __construct($name) {
$this->name = $name;
}
}
function createUser() {
$user = new User("Ahmed");
echo $user->name;
} // <- $user goes out of scope here, memory freed
createUser();
?>
✅ After the function ends, $user
is destroyed and memory is released.
❌ But if there’s a circular reference inside objects, GC kicks in.
🌀 2. Circular References in Classes
<?php
class A {
public $ref;
}
class B {
public $ref;
}
$a = new A();
$b = new B();
$a->ref = $b;
$b->ref = $a; // <- circular reference
unset($a, $b); // memory NOT immediately freed
gc_collect_cycles(); // manually free cycles
?>
➡️ Without GC, long-running scripts (like workers) would slowly eat up RAM.
📄 3. PDF Generation Example (Memory Heavy)
Suppose you’re using dompdf or TCPDF in Laravel/PHP.
<?php
use Dompdf\Dompdf;
function generatePDF($data) {
$dompdf = new Dompdf();
$dompdf->loadHtml("<h1>Hello $data</h1>");
$dompdf->render();
$pdf = $dompdf->output();
file_put_contents("output.pdf", $pdf);
// Free memory explicitly
unset($dompdf, $pdf);
gc_collect_cycles(); // cleanup
}
generatePDF("Ahmed");
?>
⚠️ Problem: If you generate thousands of PDFs in a queue job without cleanup, memory keeps rising.
✅ Solution: unset()
variables, call gc_collect_cycles()
occasionally.
📊 4. Excel Generation Example (Maatwebsite / PhpSpreadsheet)
<?php
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
function generateExcel() {
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$sheet->setCellValue('A1', 'Hello Ahmed');
$writer = new Xlsx($spreadsheet);
$writer->save('output.xlsx');
// Free memory
$spreadsheet->disconnectWorksheets();
unset($spreadsheet, $writer);
gc_collect_cycles();
}
generateExcel();
?>
⚠️ If you create 100,000+ rows without cleaning up, memory will spike (hundreds of MBs).
✅ Best practice:
- Use chunked writes (write in batches instead of loading everything in memory).
- Always
unset()
objects and callgc_collect_cycles()
in long-running scripts.
🚀 5. Real-World Tips (Laravel Jobs / Workers)
- For short web requests: memory is freed at the end of the request anyway.
-
For long-running jobs (queues, daemons, APIs that loop over big datasets):
- Always
unset()
large objects after use. - Call
gc_collect_cycles()
every few iterations. - For Excel, prefer
->store()
with queue exports (Maatwebsite handles memory better). - For PDF, generate and store in chunks instead of bulk in one go.
- Always
Let’s clear the confusion — why do we need to unset()
and call gc_collect_cycles()
in PDF (or Excel) generation, even though variables go out of scope?
🧠 1. Normal Case – Variables Go Out of Scope
In a short script or a web request, PHP automatically:
- Destroys variables when they go out of scope
- Frees memory when the request ends
Example:
function test() {
$a = "hello"; // allocated
} // <- function ends, $a destroyed, memory freed
➡️ Nothing special needed.
🧾 2. Why PDF/Excel is Different
Libraries like Dompdf, TCPDF, PhpSpreadsheet are heavy:
- They create big objects in memory (fonts, images, large buffers, XML tree for Excel, etc.)
- Some of these objects reference each other (circular references)
- PHP’s normal “out of scope” cleanup can’t immediately free all memory because circular references stay alive until GC runs
So, if you’re generating 1 PDF in a request, it doesn’t matter — memory is freed at request end.
But if you’re generating 100 PDFs in a loop (long-running worker), memory keeps growing until GC decides to run.
🔄 3. Example: PDF Without Explicit Cleanup
use Dompdf\Dompdf;
for ($i = 0; $i < 100; $i++) {
$dompdf = new Dompdf();
$dompdf->loadHtml("<h1>PDF $i</h1>");
$dompdf->render();
file_put_contents("pdf_$i.pdf", $dompdf->output());
// no unset or GC
}
⚠️ Problem: Memory grows with each loop because internal buffers & objects remain in circular references.
Eventually → script crashes with Out of Memory error.
✅ 4. With Explicit Cleanup
use Dompdf\Dompdf;
for ($i = 0; $i < 100; $i++) {
$dompdf = new Dompdf();
$dompdf->loadHtml("<h1>PDF $i</h1>");
$dompdf->render();
file_put_contents("pdf_$i.pdf", $dompdf->output());
// Cleanup
unset($dompdf);
gc_collect_cycles(); // force GC to break cycles
}
✔️ Memory stays stable, script runs fine.
⚡ 5. Key Difference
- Short script/web request → memory is freed when request ends (no worries).
- Long-running jobs (queue workers, daemons, batch processing) → memory accumulates unless you explicitly free heavy objects.
📝 Rule of Thumb
- For normal requests: don’t bother, PHP cleans up.
- For batch jobs / loops / workers: always
unset()
heavy objects and occasionally callgc_collect_cycles()
.
👉 That’s why for PDF/Excel generation in bulk, we explicitly free memory — not because PHP won’t free it at all, but because waiting until the script ends is too late for long-running processes.
✅ In summary:
- Classes & methods free memory normally.
- Garbage Collection is important when objects reference each other (circular refs).
- For PDF/Excel generation, explicitly free memory (
unset
,disconnectWorksheets
,gc_collect_cycles
) to prevent leaks. - In queues/long-running jobs, this is critical to avoid servers crashing from memory bloat.
Top comments (0)