Blogs

Welcome to the BackBenchers Blog – your go-to resource for insights, tips, and updates in the world of IT and tech education. Here, we share industry trends, practical coding tutorials, career advice, and success stories from our graduates. Whether you’re just starting your tech journey or looking to sharpen your skills, our blog offers valuable content to keep you informed and inspired. Stay tuned for regular updates and expert perspectives to help you navigate the dynamic landscape of information technology.

17 September 2024

Java is well-known for its “write once, run anywhere” philosophy, and at the heart of this capability lies the Java Virtual Machine (JVM). However, the JVM’s efficiency relies heavily on how it manages memory and resources. In this blog, we’ll take a comprehensive look at how memory is allocated in the JVM, how static objects and variables behave, and how garbage collection works.

By the end of this post, you’ll have a good understanding of:

  • JVM Components and their roles in memory management.
  • How static, instance, local variables and objects are allocated and behave in Java.
  • The difference between heap and native memory, and why Metaspace was introduced.
  • How garbage collection works in Java, focusing on object lifecycles
  1. Java Virtual Machine (JVM) Components

When you run a Java program, the JVM creates an execution environment for it. This environment manages various resources, such as memory and processor cycles. The key components of the JVM related to memory management are:

Before Java 8, PermGen was part of the Java heap and had a fixed size. It was used to store class metadata, such as class definitions, method bytecode, and static variables. One of the major drawbacks of PermGen was that it was prone to running out of memory in environments that dynamically loaded many classes (such as application servers), which could lead to OutOfMemoryError: PermGen space because of its fixed size.

In Java 8, the introduction of Metaspace moved class metadata storage out of the heap and into native memory. This change allowed for dynamic growth of memory used for class metadata, making it more flexible and reducing the likelihood of out-of-memory errors.

Memory Types

Memory AreaTypeDescription
Java HeapJVM-managedStores Java objects (managed by the JVM’s garbage collector).
C HeapNative MemoryMemory allocated by the JVM and native libraries (using malloc/free or similar mechanisms).
MetaspaceNative MemoryStores class metadata (in native memory, not managed by JVM GC, dynamically grows).
Thread StackNative MemoryStores method call frames and local variables for each thread (allocated in native memory).
  1. Class Loading and Static Initialization

Java has a dynamic class-loading mechanism, meaning that classes are loaded into memory when they are first referenced in the program. Once a class is loaded, the JVM stores its metadata in Metaspace.

When the JVM loads a class, any static variables or static blocks are initialized. Here’s how static variables behave:

  • Static variables are stored in Metaspace, and they are associated with the class rather than any specific instance.
  • If a static variable references an object, the object itself is allocated in the heap, and the reference to the object is stored in the Method Area.
  1. How Static Variables and Objects Are Stored

One common misconception is that static objects are stored in the Method Area. This is not correct. Static variables or references are stored in the Method Area, but the objects they reference are stored in the heap.

Static Variable Storage

  • Static variables: Stored in the Method Area (Metaspace).
  • Static object: Allocated in the heap, but the reference to the object is stored in the Method Area.

This distinction is critical because even though the static reference is persistent throughout the program’s runtime, the object itself resides in the heap and is managed by the JVM’s garbage collector.

  1. Garbage Collection and Static Objects

The garbage collector in Java reclaims memory from the heap by identifying objects that are no longer reachable from the root references. These root references include:

  • Active stack frames (local variables, object references).
  • Static variables in the Method Area.
  • Thread references and native references.

Garbage Collection Process

  • Mark Phase: The garbage collector starts by identifying “live” objects that are still referenced by the current running method on stack and other objects which are referenced by this object. Objects which were created by methods which are popped out of stack is no longer have any reference pointing to it, so can be marked for garbage collection.
  • Sweep Phase: Unreferenced objects are then marked for removal, and their memory is reclaimed.

Static Objects and Garbage Collection

  • Static objects will never be garbage collected as long as the class is loaded, because they are always referenced by a static variable stored in the Method Area. One exception is that if we make static object as null, in that case garbage collector will check that static object reference at metaspace is null, and will delete that object.
  • Non-static objects, created within methods or local contexts, are eligible for garbage collection when they go out of scope and are no longer referenced.

Conclusion

Understanding how static objects, heap memory, and native memory (Metaspace) work together is crucial for writing efficient Java applications.