Garbage Collection

Garbage collection is one of the major problem, that is being ignored by most of the Java developers. However, knowing the impact of garbage collection will help you to improve your art of coding in performance critical applications. This article explains the necessity of garbage collection and the best practices to follow when you write such applications. Let's start this article with a simple C application.

Copy and paste the following code in a C source file named as application.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main() {
    int i;
    for (i = 0; i < 10000; i++) {
        // Allocate the memory
        char *name = malloc(10000000000);
        if (name == 0) {
            printf("ERROR: Out of memory\n");
            return 1;
        }
        // Assign a value and print it
        strcpy(name, "Hello, world!");
        printf("%s\n", name);
    }
    // Pause the application
    getchar();
    return 0;
}
In this code, malloc command allocates a memory location with 10,000,000,000 bytes and the message "Hello World" is stored in that location. It continues for 10,000 times.

Compile the source code.
gcc  application.c

Run the program.
/.a.out

Depending on your system configuration, either you may get Out of memory error or 10,000 "Hello World" messages. In my computer (Ubuntu 64 bit, Intel Core i3 with 8 GB memory), it prints 10,000 messages.

Following screen shot shows the memory acquired by this C application at the end. As you can see, it consumed 77.1 MiB.


Now modify the source code by adding free(name); command after the printf statement. This command is used to free the memory allocated by malloc.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main() {
    int i;
    for (i = 0; i < 10000; i++) {
        // Allocate the memory
        char *name = malloc(10000000000);
        if (name == 0) {
            printf("ERROR: Out of memory\n");
            return 1;
        }
        // Assign a value and print it
        strcpy(name, "Hello, world!");
        printf("%s\n", name);
        // Free the memory
        free(name);
    }
    // Pause the application
    getchar();
    return 0;
}
Compile and run the application again.


This time our application consumed just 96.0 KiB. If you increase the number of iterations (in other words, number of garbage memory allocations), you may get Memory out of error. This example clearly shows the impact of garbage objects in memory management. In a language which does not provide automatic garbage collection, you must be aware of what you are doing. If not, your application may throw memory out of error and terminate.

What is garbage?
In computing, garbage is an unwanted or unused memory allocation. In the perspective of C (or any other languages which provide direct memory access), if you allocate a memory and if you do not want to use it anymore, you must free that memory. If not, the unused memory allocation is identified as garbage. In the context of Java, if there is an unreachable object, that is known as garbage (Primitives are not considered as garbage).

In Java, objects are created in the heap and their reference is returned to you. For example take the following code:
public class StudentDemo {
    public static void main(String[] args) {
        Student stu = new Student(100);
    }
}

class Student {
    int index;

    public Student(int index) {
        this.index = index;
    }
}
If you run this code, a Student object with index number 100 will be created in the heap and a reference will be created in the stack as shown below.


Now, if we say stu = null;, the reference will be removed but the actual student object will be there in the memory. This unreachable objects is called garbage.
public class StudentDemo {
    public static void main(String[] args) {
        Student stu = new Student(100);
        stu = null;
    }
}

class Student {
    int index;

    public Student(int index) {
        this.index = index;
    }
}


Reassigning an existing reference also create garbage objects. In the following code, a new Student object is assigned to the existing stu reference. After this statement, the previous Student object (with index number 100) becomes garbage.
public class StudentDemo {
    public static void main(String[] args) {
        Student stu = new Student(100);
        stu = new Student(101);
    }
}

class Student {
    int index;

    public Student(int index) {
        this.index = index;
    }
}


There can be garbage objects with references (but still unreachable). Look at the following code.
public class IslandOfIsolation {
    public static void main(String[] args) {
        // Create the objects
        Node a = new Node(1);
        Node b = new Node(2);
        Node c = new Node(3);

        // Create internal references
        a.next = b;
        b.next = c;
        c.next = a;

        // Remove the references from main method
        a = null;
        b = null;
        c = null;

        // Now there are 3 garbage objects
    }
}

class Node {
    int val;
    Node next;

    public Node(int val) {
        this.val = val;
    }
}
According to this code, there are three nodes with internal references, but still they are unreachable because there is no way to access any of them from the main method. This kind of garbage objects are called island of isolation.

Garbage in Java is an unreachable object; not an object without reference.
How to remove garbage objects?
There is no way for a Java developer to delete garbage objects from the heap. In other words, Java does not provide any similar methods to C's free method in order to clear the memory allocation. This is where Java's automatic garbage collector comes to the stage. Java has a background thread named "Garbage Collector" which runs on demand and remove the garbage objects from the heap.

Even though, the time to clear the memory is under the control of Java Virtual Machine, Java provides two identical methods to send a request for the Java garbage collector to clear the memory.
System.gc();
Runtime.getRuntime().gc();
However, using these methods does not guarantee the execution of garbage collector, because JVM has the right to ignore your request. Therefore, it is not recommended to depend on these methods. According to my experience, I use these methods only when I teach garbage collection to my students.

Best Practices
There are two important rules regarding garbage collection.

Rule 1: Avoid unnecessary garbage objects
Producing more and more garbage leads to the garbage collector to run frequently and it will significantly reduce the performance of your application.

Example 1: String Concatenation
The bad practice:
public class Concatenation {
    public static void main(String[] args) {
        String str = "";
        for(int i = 0; i < 100; i++) {
            // String is an immutable class
            str += i;   // This line creates a new object and the old object becomes garbage
        }
        System.out.println(str);
    }
}

The best practice:
public class Concatenation {
    public static void main(String[] args) {
        StringBuilder builder = new StringBuilder();
        for(int i = 0; i < 100; i++) {
            builder.append(i);
        }
        String str = builder.toString();
        System.out.println(str);
    }
}
Reason: String is an immutable object. Modifying a String object multiple times, generates multiple garbage objects. For more details, visit to: Immutability of String

Example 2: Integer addition
The bad practice:
public class Addition {
    public static void main(String[] args) {
        Integer total = 0;
        for(int i = 0; i < 100; i++) {
            // Integer is an immutable class
            total += i;   // This line creates a new Integer object and the old object becomes garbage
        }
        System.out.println(total);
    }
}

The best practice:
public class Addition {
    public static void main(String[] args) {
        int total = 0;
        for(int i = 0; i < 100; i++) {
            // Primitives are replaced. They never become garbage
            total += i;
        }
        System.out.println(total);
    }
}
Reason: Integer (or any other wrapper class) object is an immutable object. Modifying an Integer (or other wrapper class) object multiple times generates multiple garbage objects. Use primitives instead of wrapper classes as much as possible.

Rule 2: Avoid memory leaks
Memory leak is the situation where some objects escape from garbage collection. It may lead to performance issues or MemoryOutOfError.


More details about Memory leak and other advanced garbage collection concepts will be discussed in another article.
Read More

Contact Form

Name

Email *

Message *