Lecture 22
Garbage Collection &
Dynamic Memory Management
Unit V: File Handling, Memory Management
Instructor: Mohsin F. Dar
Assistant Professor
Cloud & Software Operations Cluster | SOCS | UPES
Quick Recap
Dynamic Memory Management in C
- malloc(): Allocates uninitialized memory block
- calloc(): Allocates and initializes memory to zero
- realloc(): Resizes previously allocated memory
- free(): Deallocates memory and returns it to heap
Key Point: In C, the programmer is responsible for both allocation and deallocation of memory. There is NO automatic garbage collection!
What is Garbage Collection?
Definition
Garbage Collection (GC) is an automatic memory management feature that identifies and reclaims memory occupied by objects that are no longer in use by the program.
In Simple Terms:
It's like having an automatic cleaner that finds and removes unused items from your computer's memory.
Languages WITH GC
- Java
- Python
- JavaScript
- C#
- Go
Languages WITHOUT GC
- C
- C++
- Rust (ownership model)
Why C Doesn't Have Garbage Collection?
Design Philosophy
- Performance: GC adds runtime overhead; C prioritizes speed
- Control: Gives programmers complete control over memory
- Predictability: No unexpected pauses from GC cycles
- Simplicity: Keeps the language simple and close to hardware
- System Programming: OS kernels, drivers need precise memory control
Trade-off: Greater power and performance comes with greater responsibility. The programmer must manually manage memory, which can lead to bugs if not done carefully.
Memory Leaks
What is a Memory Leak?
A memory leak occurs when dynamically allocated memory is not freed, causing the program to consume more and more memory over time.
Example of Memory Leak:
void createLeak() {
int *ptr = (int*) malloc(sizeof(int) * 100);
}
int main() {
for(int i = 0; i < 1000; i++) {
createLeak();
}
return 0;
}
Consequence: Program gradually consumes all available memory, leading to crashes or system slowdown.
Dangling Pointers
What is a Dangling Pointer?
A dangling pointer is a pointer that references memory that has already been freed or is invalid.
Example of Dangling Pointer:
int main() {
int *ptr = (int*) malloc(sizeof(int));
*ptr = 42;
free(ptr);
printf("%d", *ptr);
return 0;
}
Danger: Accessing dangling pointers can cause crashes, data corruption, or security vulnerabilities!
Best Practice: Set pointer to NULL after freeing: ptr = NULL;
Double Free Error
What is Double Free?
A double free occurs when free() is called twice on the same memory address.
Example of Double Free:
int main() {
int *ptr = (int*) malloc(sizeof(int));
*ptr = 100;
free(ptr);
free(ptr);
return 0;
}
Consequence: Heap corruption, program crash, or security vulnerabilities.
Prevention:
free(ptr);
ptr = NULL;
if(ptr != NULL) {
free(ptr);
}
Best Practices for Memory Management
Golden Rules
- Always check if malloc/calloc succeeded
int *ptr = (int*) malloc(sizeof(int));
if(ptr == NULL) {
fprintf(stderr, "Memory allocation failed\n");
return -1;
}
- Every malloc needs a corresponding free
- Set pointers to NULL after freeing
- Never access memory after freeing it
- Free memory in reverse order of dependencies
Problem 1: Dynamic Array
Problem:
Create a program that dynamically allocates an array of integers, fills it with user input, displays the values, and properly frees the memory.
Solution:
#include <stdio.h>
#include <stdlib.h>
int main() {
int n, i;
int *arr;
printf("Enter number of elements: ");
scanf("%d", &n);
arr = (int*) malloc(n * sizeof(int));
if(arr == NULL) {
printf("Memory allocation failed!\n");
return -1;
}
for(i = 0; i < n; i++) {
printf("Enter element %d: ", i+1);
scanf("%d", &arr[i]);
}
printf("\nArray elements: ");
for(i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
free(arr);
arr = NULL;
return 0;
}
Problem 2: Dynamic 2D Array
Problem:
Allocate a 2D array dynamically (matrix) and properly deallocate it.
Solution:
#include <stdio.h>
#include <stdlib.h>
int main() {
int rows = 3, cols = 4, i, j;
int **matrix;
matrix = (int**) malloc(rows * sizeof(int*));
for(i = 0; i < rows; i++) {
matrix[i] = (int*) malloc(cols * sizeof(int));
}
for(i = 0; i < rows; i++) {
for(j = 0; j < cols; j++) {
matrix[i][j] = i * cols + j;
}
}
for(i = 0; i < rows; i++) {
free(matrix[i]);
}
free(matrix);
return 0;
}
Problem 3: Dynamic String Handling
Problem:
Create a function that dynamically allocates memory for a string, copies user input, and returns it. The caller is responsible for freeing the memory.
Solution:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char* getString() {
char temp[100];
char *str;
printf("Enter a string: ");
fgets(temp, 100, stdin);
str = (char*) malloc(strlen(temp) + 1);
if(str == NULL) {
return NULL;
}
strcpy(str, temp);
return str;
}
int main() {
char *myString = getString();
if(myString != NULL) {
printf("You entered: %s", myString);
free(myString);
myString = NULL;
Problem 4: Resizing Array with realloc
Problem:
Start with an array of 5 integers, then dynamically resize it to hold 10 integers without losing existing data.
Solution:
#include <stdio.h>
#include <stdlib.h>
int main() {
int *arr;
int i;
arr = (int*) malloc(5 * sizeof(int));
for(i = 0; i < 5; i++) {
arr[i] = i + 1;
}
printf("Initial array: ");
for(i = 0; i < 5; i++) {
printf("%d ", arr[i]);
}
arr = (int*) realloc(arr, 10 * sizeof(int));
if(arr == NULL) {
printf("Reallocation failed!\n");
return -1;
}
for(i = 5; i < 10; i++) {
arr[i] = i + 1;
}
printf("\nResized array: ");
for(i = 0; i < 10; i++) {
printf("%d ", arr[i]);
}
free(arr);
return 0;
Problem 5: Linked List Memory Management
Problem:
Create a simple linked list, add nodes dynamically, and properly free all nodes.
Solution:
#include <stdio.h>
#include <stdlib.h>
struct Node {
int data;
struct Node *next;
};
struct Node* createNode(int data) {
struct Node *newNode = (struct Node*) malloc(sizeof(struct Node));
if(newNode == NULL) {
printf("Memory allocation failed!\n");
return NULL;
}
newNode->data = data;
newNode->next = NULL;
return newNode;
}
void freeList(struct Node *head) {
struct Node *temp;
while(head != NULL) {
temp = head;
head = head->next;
free(temp);
}
}
int main() {
struct Node *head = createNode(10);
head->next = createNode(20);
head->next->next = createNode(30);
struct Node *temp = head;
printf("List: ");
while(temp != NULL) {
printf("%d -> ", temp->data);
temp = temp->next;
}
printf("NULL\n");
freeList(head);
return 0;
Common Memory Management Mistakes
Top Mistakes to Avoid
1. Forgetting to Free Memory
void badFunction() {
int *ptr = malloc(100 * sizeof(int));
}
2. Using Freed Memory
int *ptr = malloc(sizeof(int));
free(ptr);
*ptr = 10;
3. Not Checking malloc Return Value
int *ptr = malloc(sizeof(int));
*ptr = 5;
4. Freeing Stack Memory
int x = 10;
int *ptr = &x;
free(ptr);
Problem 6: Student Database
Problem:
Create a dynamic array of student structures. Allow user to input student data and properly manage memory.
Solution:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Student {
char name[50];
int rollNo;
float marks;
};
int main() {
int n, i;
struct Student *students;
printf("Enter number of students: ");
scanf("%d", &n);
students = (struct Student*) malloc(n * sizeof(struct Student));
if(students == NULL) {
printf("Memory allocation failed!\n");
return -1;
}
for(i = 0; i < n; i++) {
printf("\nStudent %d:\n", i+1);
printf("Name: ");
scanf("%s", students[i].name);
printf("Roll No: ");
scanf("%d", &students[i].rollNo);
printf("Marks: ");
scanf("%f", &students[i].marks);
}
printf("\n\nStudent Records:\n");
for(i = 0; i < n; i++) {
printf("%s\t%d\t%.2f\n",
students[i].name,
students[i].rollNo,
students[i].marks);
}
free(students);
students = NULL;
return 0;
}
Memory Debugging Tools
Tools to Detect Memory Issues
1. Valgrind (Linux/Mac)
Usage: valgrind --leak-check=full ./program
Detects: Memory leaks, invalid memory access, use after free
2. Address Sanitizer (ASan)
Compilation: gcc -fsanitize=address program.c
Detects: Buffer overflows, use after free, memory leaks
3. Dr. Memory (Windows)
Free tool for Windows that detects memory errors including:
- Memory leaks
- Use after free
- Buffer overflows
- Uninitialized memory access
4. Manual Techniques
- Print statements before/after malloc and free
- Keep track of allocations in a separate log
- Use assert() to verify assumptions
- Set pointers to NULL after freeing
Problem 7: Find and Fix Memory Issues
Problem:
Identify and fix ALL memory issues in the following code:
#include <stdio.h>
#include <stdlib.h>
int main() {
int *arr1 = malloc(5 * sizeof(int));
int *arr2 = malloc(3 * sizeof(int));
arr1[0] = 10;
free(arr1);
printf("%d", arr1[0]);
free(arr2);
free(arr2);
return 0;
}
Issues Found:
- Missing cast for malloc
- Not checking if malloc succeeded
- Using arr1 after freeing (dangling pointer)
- Double free on arr2
- Not setting pointers to NULL after free
Corrected Code:
#include <stdio.h>
#include <stdlib.h>
int main() {
int *arr1 = (int*)malloc(5 * sizeof(int));
int *arr2 = (int*)malloc(3 * sizeof(int));
if(arr1 == NULL || arr2 == NULL) {
printf("Allocation failed!\n");
return -1;
}
arr1[0] = 10;
printf("%d", arr1[0]);
free(arr1);
arr1 = NULL;
free(arr2);
arr2 = NULL;
return 0;
}
Memory Layout of a C Program
Understanding Where Memory is Allocated
| Memory Segment |
Contains |
Management |
| Stack |
Local variables, function parameters, return addresses |
Automatic (LIFO) |
| Heap |
Dynamically allocated memory (malloc, calloc) |
Manual (programmer) |
| Data Segment |
Global & static variables (initialized) |
Automatic |
| BSS Segment |
Uninitialized global & static variables |
Automatic |
| Text Segment |
Program code (instructions) |
Read-only |
Key Point: Only heap memory requires manual management using malloc/calloc/realloc/free!
Problem 8: Dynamic String Array
Problem:
Create a program that reads n strings from user, stores them dynamically, sorts them, and displays them. Properly free all memory.
Solution (Part 1):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
int n, i, j;
char **strings;
char temp[100], *tempPtr;
printf("Enter number of strings: ");
scanf("%d", &n);
getchar();
strings = (char**) malloc(n * sizeof(char*));
if(strings == NULL) {
printf("Memory allocation failed!\n");
return -1;
}
for(i = 0; i < n; i++) {
printf("Enter string %d: ", i+1);
fgets(temp, 100, stdin);
temp[strcspn(temp, "\n")] = 0;
strings[i] = (char*) malloc((strlen(temp) + 1) * sizeof(char));
strcpy(strings[i], temp);
}
Problem 8: Solution (Part 2)
for(i = 0; i < n-1; i++) {
for(j = 0; j < n-i-1; j++) {
if(strcmp(strings[j], strings[j+1]) > 0) {
tempPtr = strings[j];
strings[j] = strings[j+1];
strings[j+1] = tempPtr;
}
}
}
printf("\nSorted strings:\n");
for(i = 0; i < n; i++) {
printf("%d. %s\n", i+1, strings[i]);
}
for(i = 0; i < n; i++) {
free(strings[i]);
}
free(strings);
return 0;
}
Tips for Exams & Practical
Memory Management Checklist
✓ Before Writing Code:
- Plan which data needs dynamic allocation
- Calculate exact memory needed
- Identify when memory should be freed
- Design clear ownership of memory
✓ While Writing Code:
- Always cast malloc/calloc return value
- Check if allocation succeeded (NULL check)
- Use sizeof() instead of hardcoded sizes
- Match every malloc with a free
- Set pointer to NULL after freeing
✓ After Writing Code:
- Count malloc calls = free calls?
- Check for memory leaks in all code paths
- Verify no use-after-free
- Test with different inputs
- Use tools like Valgrind/ASan
✓ Common Mistakes to Avoid:
- Forgetting to free memory
- Using memory after freeing it
- Not checking for allocation failures
- Memory leaks in error paths
- Buffer overflows/underflows
Summary
Key Takeaways
What We Learned:
- Garbage Collection: Automatic memory management (not in C)
- Memory Leaks: Allocated memory not freed
- Dangling Pointers: Pointers to freed memory
- Double Free: Freeing same memory twice
- Best Practices: Check malloc, free properly, set NULL
Memory Management is Critical For:
- Preventing crashes and bugs
- Efficient resource usage
- Professional-quality code
- System stability
- Security & performance
The Golden Rule of Memory Management:
Every malloc() must have a corresponding free()
Remember to:
- Check for NULL after allocation
- Free in the reverse order of allocation
- Set pointers to NULL after freeing
- Use tools to verify memory safety
Practice Questions for Students
Conceptual Questions:
- Why doesn't C have automatic garbage collection?
- What is the difference between a memory leak and a dangling pointer?
- When should you use malloc vs calloc?
- What happens if you free() the same pointer twice?
Practical Assignments:
- Write a program to implement a dynamic stack using linked list
- Create a phone book application using dynamic arrays
- Implement a function to concatenate two dynamic strings
- Build a simple memory leak detector that tracks allocations
Tip: Practice these problems and always run your code through valgrind or similar tools to check for memory issues!
Thank You!
Questions?
Mohsin F. Dar
Assistant Professor
Cloud & Software Operations Cluster | SOCS
UPES
"Good programmers manage memory.
Great programmers manage it perfectly."