eBook – Guide Spring Cloud – NPI EA (cat=Spring Cloud)
announcement - icon

Let's get started with a Microservice Architecture with Spring Cloud:

>> Join Pro and download the eBook

eBook – Mockito – NPI EA (tag = Mockito)
announcement - icon

Mocking is an essential part of unit testing, and the Mockito library makes it easy to write clean and intuitive unit tests for your Java code.

Get started with mocking and improve your application tests using our Mockito guide:

Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Reactive – NPI EA (cat=Reactive)
announcement - icon

Spring 5 added support for reactive programming with the Spring WebFlux module, which has been improved upon ever since. Get started with the Reactor project basics and reactive programming in Spring Boot:

>> Join Pro and download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Jackson – NPI EA (cat=Jackson)
announcement - icon

Do JSON right with Jackson

Download the E-book

eBook – HTTP Client – NPI EA (cat=Http Client-Side)
announcement - icon

Get the most out of the Apache HTTP Client

Download the E-book

eBook – Maven – NPI EA (cat = Maven)
announcement - icon

Get Started with Apache Maven:

Download the E-book

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

eBook – RwS – NPI EA (cat=Spring MVC)
announcement - icon

Building a REST API with Spring?

Download the E-book

Course – LS – NPI EA (cat=Jackson)
announcement - icon

Get started with Spring and Spring Boot, through the Learn Spring course:

>> LEARN SPRING
Course – RWSB – NPI EA (cat=REST)
announcement - icon

Explore Spring Boot 3 and Spring 6 in-depth through building a full REST API with the framework:

>> The New “REST With Spring Boot”

Course – LSS – NPI EA (cat=Spring Security)
announcement - icon

Yes, Spring Security can be complex, from the more advanced functionality within the Core to the deep OAuth support in the framework.

I built the security material as two full courses - Core and OAuth, to get practical with these more complex scenarios. We explore when and how to use each feature and code through it on the backing project.

You can explore the course here:

>> Learn Spring Security

Course – LSD – NPI EA (tag=Spring Data JPA)
announcement - icon

Spring Data JPA is a great way to handle the complexity of JPA with the powerful simplicity of Spring Boot.

Get started with Spring Data JPA through the guided reference course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (cat=Spring Boot)
announcement - icon

Refactor Java code safely — and automatically — with OpenRewrite.

Refactoring big codebases by hand is slow, risky, and easy to put off. That’s where OpenRewrite comes in. The open-source framework for large-scale, automated code transformations helps teams modernize safely and consistently.

Each month, the creators and maintainers of OpenRewrite at Moderne run live, hands-on training sessions — one for newcomers and one for experienced users. You’ll see how recipes work, how to apply them across projects, and how to modernize code with confidence.

Join the next session, bring your questions, and learn how to automate the kind of work that usually eats your sprint time.

Course – LJB – NPI EA (cat = Core Java)
announcement - icon

Code your way through and build up a solid, practical foundation of Java:

>> Learn Java Basics

Partner – LambdaTest – NPI EA (cat= Testing)
announcement - icon

Distributed systems often come with complex challenges such as service-to-service communication, state management, asynchronous messaging, security, and more.

Dapr (Distributed Application Runtime) provides a set of APIs and building blocks to address these challenges, abstracting away infrastructure so we can focus on business logic.

In this tutorial, we'll focus on Dapr's pub/sub API for message brokering. Using its Spring Boot integration, we'll simplify the creation of a loosely coupled, portable, and easily testable pub/sub messaging system:

>> Flexible Pub/Sub Messaging With Spring Boot and Dapr

1. Introduction

In this tutorial, we’ll learn how garbage collection treats static fields. Also, we’ll touch on such topics as class loading and class objects. After this article, we’ll understand better the connection between classes, classloaders, and static fields and how the garbage collector treats them.

2. Overview of Garbage Collection in Java

Java provides a pretty nice feature of automatic memory management. This approach, in most cases, isn’t as efficient as the manual. However, it helps to avoid hard-to-debug problems and reduces boilerplate code. Also, with the improvements in garbage collection, the process becomes better and better. Thus, we should review how the garbage collector works and what garbage is in our applications.

2.1. Garbage Objects

Reference counting is the most straightforward and intuitive way to identify garbage objects. This approach allows us to check if our current object has any references to it. However, this approach has some drawbacks, and the most significant one is cyclic references.

One of the methods to deal with cyclic references is tracing. Objects turn into garbage when they don’t have any links to the garbage collection roots of the application.

2.2. Static Fields and Class Objects

In Java, everything is an Object, including the definition of classes. They contain all the meta information about a class, methods, and the values of static fields. Thus, all the static fields are references by respected class objects. Therefore, until the class object exists and references by the application, static fields won’t be eligible for garbage collection.

At the same time, all the loaded classes have the reference to the classloader used for loading this particular class. This way, we can keep track of the loaded classes.

In this case, we have a hierarchy of references. A classloader keeps the reference to all the loaded classes. At the same time, classes store the reference to the respective classloader. We have two-way referencing in this case. Whenever we’re instantiating a new object, it will hold a reference to the definition of its classes. Thus, we have the following hierarchy:

classloader diagram

We cannot unload a class until our application has a reference to it. Let’s check what we need to make a class definition eligible for garbage collection. First, there should be no references from an application to the instances of a class. It’s important because all the instances bear references to their class. Second, the class loader for this class should be unavailable from an application. And lastly, the class itself should have no references in an application.

3. Example of a Garbage Collected Static Field

Let’s create an example in which we make a garbage collector remove our static field. JVM supports class unloading for the classes loaded by extension and system class loaders. However, this will be hard to reproduce, and we’ll use a custom class loader for this as we’ll have more control over it.

3.1. Custom Class Loader

First, let’s create our own CustomClassloader will load a class from a resource folder of our application. For our classloader to work, we should override the loadClass(String name) method:

public class CustomClassloader extends ClassLoader {

    public static final String PREFIX = "com.baeldung.classloader";

    public CustomClassloader(ClassLoader parent) {
        super(parent);
    }

    @Override
    public Class<?> loadClass(String name) throws ClassNotFoundException {
        if (name.startsWith(PREFIX)) {
            return getClass(name);
        } else {
            return super.loadClass(name);
        }
    }

    ...
}

In this implementation, we’re using the getClass method, which hides the complexity of loading a class from resources:

private Class<?> getClass(String name) {
    String fileName = name.replace('.', File.separatorChar) + ".class";
    try {
        byte[] byteArr = IOUtils.toByteArray(getClass().getClassLoader().getResourceAsStream(fileName));
        Class<?> c = defineClass(name, byteArr, 0, byteArr.length);
        resolveClass(c);
        return c;
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

3.2. Folder Structure

To work correctly, our custom class should be outside the scope of our class path. This way, it won’t be uploaded by a system class loader. The only classloader working with this particular class will be our CustomClassloader.

The structure of our folders will look like this:

Screen-Shot-2022-09-05-at-10.46.21

3.3. Static Field Holder

We’ll use a custom class that will play the role of a holder for our static field. After defining our implementation of a classloader, we can use it to upload a class we’ve prepared. It’s a simple class:

public class GarbageCollectedStaticFieldHolder {

    private static GarbageCollectedInnerObject garbageCollectedInnerObject =
      new GarbageCollectedInnerObject("Hello from a garbage collected static field");

    public void printValue() {
        System.out.println(garbageCollectedInnerObject.getMessage());
    }
}

3.4. Static Field Class

GarbageCollectedInnerObject will represent an object that we want to turn into garbage. This class, for simplicity and convenience defined in the same file as GarbageCollectedStaticFieldHolder. This class contains a message and also has overridden finalize() method. Although the finalize() method is deprecated and has many drawbacks, it will allow us to visualize when the garbage collector removes the object. We’ll use this method only for presentation purposes. Here’s our class for a static field:

class GarbageCollectedInnerObject {

    private final String message;

    public GarbageCollectedInnerObject(final String message) {
        this.message = message;
    }

    public String getMessage() {
        return message;
    }

    @Override
    protected void finalize() {
        System.out.println("The object is garbage now");
    }
}

3.5. Uploading a Class

Now we can upload and instantiate our class. After creating an instance, we can ensure that the class was uploaded, the object created, and the static field contains the needed information:

private static void loadClass() {
    try {
        final String className = "com.baeldung.classloader.GarbageCollectedStaticFieldHolder";
        CustomClassloader loader = new CustomClassloader(Main.class.getClassLoader());
        Class<?> clazz = loader.loadClass(className);
        Object instance = clazz.getConstructor().newInstance();
        clazz.getMethod(METHOD_NAME).invoke(instance);
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

This method should create the instance of our special class and output the message:

Hello from a garbage collected static field

3.6. Garbage Collection in Action

Now let’s start our application and try to remove the garbage:

public static void main(String[] args) throws InterruptedException {
    loadClass();
    System.gc();
    Thread.sleep(1000);
}

After calling the method loadClass(), all the variables inside this method, namely, classloader, our class, and instance, will go out of scope and lose the connection with the garbage collection roots. It’s also possible to assign null to the references, but the option to use scope is cleaner:

public static void main(String[] args) throws InterruptedException {
    CustomClassloader loader;
    Class<?> clazz;
    Object instance;
    try {
        final String className = "com.baeldung.classloader.GarbageCollectedStaticFieldHolder";
        loader = new CustomClassloader(GarbageCollectionNullExample.class.getClassLoader());
        clazz = loader.loadClass(className);
        instance = clazz.getConstructor().newInstance();
        clazz.getMethod(METHOD_NAME).invoke(instance);
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
    loader = null;
    clazz = null;
    instance = null;
    System.gc();
    Thread.sleep(1000);
}

Even though we have some problems with this code, it’ll work in most cases. The main issue is that we cannot force garbage collection in Java, and the invocation of  System.gc() won’t guarantee that the garage collection will happen. However, in most JVM implementations, this will trigger Major Garbage Collection. Thus, we should see the following lines in the output:

Hello from a garbage collected static field 
The object is garbage now

This output shows us that the garbage collector removed the static field. The garbage collector also removed the classloader, the classes for the holder, the static field, and the connected objects.

3.7. Example Without System.gc()

We also can trigger garbage collection more naturally. This way will work more stable. However, it will require more cycles to invoke the garbage collector:

public static void main(String[] args) {
    while (true) {
        loadClass();
    }
}

Here we’re using the same loadClass() method, but we don’t invoke System.gc(), and the garbage collector is triggered when we run out of memory because we’re loading the class in an infinite loop.

4. Conclusion

This article taught us how garbage collection works in Java regarding classes and static fields. We created a custom class loader and used it for our example. Also, we learned the connection between classloaders, classes, and their static fields. For further understanding, it’s worth going through the articles linked in the texts.

The code backing this article is available on GitHub. Once you're logged in as a Baeldung Pro Member, start learning and coding on the project.
Baeldung Pro – NPI EA (cat = Baeldung)
announcement - icon

Baeldung Pro comes with both absolutely No-Ads as well as finally with Dark Mode, for a clean learning experience:

>> Explore a clean Baeldung

Once the early-adopter seats are all used, the price will go up and stay at $33/year.

eBook – HTTP Client – NPI EA (cat=HTTP Client-Side)
announcement - icon

The Apache HTTP Client is a very robust library, suitable for both simple and advanced use cases when testing HTTP endpoints. Check out our guide covering basic request and response handling, as well as security, cookies, timeouts, and more:

>> Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

Course – LS – NPI EA (cat=REST)

announcement - icon

Get started with Spring Boot and with core Spring, through the Learn Spring course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (tag=Refactoring)
announcement - icon

Modern Java teams move fast — but codebases don’t always keep up. Frameworks change, dependencies drift, and tech debt builds until it starts to drag on delivery. OpenRewrite was built to fix that: an open-source refactoring engine that automates repetitive code changes while keeping developer intent intact.

The monthly training series, led by the creators and maintainers of OpenRewrite at Moderne, walks through real-world migrations and modernization patterns. Whether you’re new to recipes or ready to write your own, you’ll learn practical ways to refactor safely and at scale.

If you’ve ever wished refactoring felt as natural — and as fast — as writing code, this is a good place to start.

eBook Jackson – NPI EA – 3 (cat = Jackson)
2 Comments
Oldest
Newest
Inline Feedbacks
View all comments