DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • All You Need To Know About Garbage Collection in Java
  • Java Memory Management
  • Choosing the Best Garbage Collection Algorithm for Better Performance in Java
  • Java Z Garbage Collector (ZGC): Revolutionizing Memory Management

Trending

  • Medallion Architecture: Efficient Batch and Stream Processing Data Pipelines With Azure Databricks and Delta Lake
  • Understanding and Mitigating IP Spoofing Attacks
  • Enhancing Security With ZTNA in Hybrid and Multi-Cloud Deployments
  • Automating Data Pipelines: Generating PySpark and SQL Jobs With LLMs in Cloudera
  1. DZone
  2. Coding
  3. Languages
  4. How to Test Code in the Garbage Collector

How to Test Code in the Garbage Collector

Want to learn more about using phantom references in Java? Take a look at this post where we explore how to test code in the garbage collector.

By 
Anatoliy Korovin user avatar
Anatoliy Korovin
·
Updated Sep. 05, 18 · Tutorial
Likes (9)
Comment
Save
Tweet
Share
13.5K Views

Join the DZone community and get the full member experience.

Join For Free

strong reference

the strong reference is the most common kind of reference. we use this kind of reference every day.

for example:

foo firstreference = new foo();
foo secondreference = firstreference;

firstreference = null;
secondreference = null;
// now the gc can collect an instance of the foo, 
// which created in the first line.


the created instance of the foo class is not available for the garbage collector, while there is at least one link to this object.

weakreference

weakreference is the type of references that will be removed by the garbage collector on the next pass, if there are no other type references to the object.

you can get an object value from the weakreference until the gc decides to collect the object. as soon as the gc decides to do it — not after the gc finalize the object and clear an allocated memory—, you will get the null from the weakreference . this happens when the gc is just marking the object for a further processing. it is important to understand that all finalization actions are executed only after this. when we look at the phantomreference , we’ll return to this point.

let’s try to test the weakreference behavior:

@test
public void testweakaftergc() {
    // arrange
    string instance = new string("123");
    weakreference<string> reference = new weakreference<>(instance);

    // act
    instance = null;
    system.gc();

    // asserts
    assertions.assertthat(reference.get()).isnull();
}


this test was successful.

also, java provides us with the weakhashmap data structure. it’s something like a hashmap , which uses the weakreference as a key of the map. if a key of the weakhashmap becomes garbage, its entry is removed automatically.

let's take a look at how we can test a weakhashmap behavior:

@test
public void testweakmap() throws interruptedexception {
    // arrange
    weakhashmap<string, boolean> map = new weakhashmap<>();
    string instance = new string("123");
    map.put(instance, true);

    // act
    instance = null;
    gcutils.fullfinalization();

    // asserts
    assertions.assertthat(map).isempty();
}


these are the results of the weak tests:

result of weak tests

you might think that the weakhashmap is a good solution for creating a cache, but you should understand the kind of behavior you need. because, if we are talking about a cache that removes data from a storage only when we reached a memory limit, then you should look at using the softreferences .

softreference

the behavior of softreference is similar to weakreference , but the gc collect this kind of reference only when our application does not have enough of memory.

let’s try to test it:

@test
public void softtest() {
    // arrange
    string instance = new string("123323");
    softreference<string> softreference = new softreference<>(instance);
    instance = null;
    assertions.assertthat(softreference).isnotnull();
    assertions.assertthat(softreference.get()).isnotnull();

    // act
    gcutils.trytoallocateallavailablememory(); //at this line we try 
    // to get all available memory until is the outofmemoryerror thrown.
    // i used gcutils, this describes below.

    // asserts
    assertions.assertthat(softreference.get()).isnull();
}


the gc collects our softreference before we get the outofmemoryerror .

this behavior is a good reason to use softreferences as a cache for a data that is difficult to build in memory.

for example, a reading video or graphics data from a slow file storage. when your application has enough of memory, you can receive this data from the cache, but if application reaches of a memory limit, then the cache cleans. and now, you need to restore this data on the next request.

however, in many cases, you need to prefer a cache based on the lru algorithm.

phantomreference

the phantomreferences are enqueued only when the object is physically removed from memory.

the get() method of the phantomreference always returns null , especially to prevent you from being able to resurrect an almost removed object.

the phantomreference provides you with the ability to determine exactly when an object was removed from memory. in order for implementation, we need to work with a referencequeue . when the referent object of a phantomreference is removed from a memory, then the gc enqueues the phantomreference in the referencequeue , and we can poll this reference from this queue.

let’s look at the code:

@test
public void testqueuepollafterfinalizationgc() {
    // arrange
    foo foo = new foo();
    referencequeue<foo> referencequeue = new referencequeue<>();
    phantomreference<foo> phantomreference = new phantomreference<>(foo, referencequeue);

    // act
    foo = null;
    gcutils.fullfinalization();

    // asserts
    assertions.assertthat(phantomreference.isenqueued()).istrue();
    assertions.assertthat(referencequeue.poll()).isequalto(phantomreference);
}

private class foo {
    @override
    protected void finalize() throws throwable {
        system.out.println("fin!");
        super.finalize();
    }
}


i used my utility class ( gcutils.java ) to test the behavior of phantom references. these tools ensure that the gc accurately performs a full cycle of cleaning and finalization. you can find the source code of this utility on github .

for example, if you need to allocate memory for processing of a large video file, only after finish processing of a previous file and release memory (that was allocated in the previous step), then using a phantomreference is the right decision. because you can request a new part of memory exactly after the gc is released as a previously used part. so, this reduces the chance to get the outofmemoryerror when you already released a part of memory. but, the gc has not collected it yet.

finalization may not happened in a timely fashion — it is difficult to predict the number of garbage collection cycles while the object was waiting for finalization. it can lead to a serious delay in actually cleaning up garbage objects and become the reason you can get an outofmemoryerrors, even when most of the heap is garbage.

i wrote a simple utility to check a code on memory leaks, which is using phantom references.

here is an example of using this tool:

@test
public void testwithoutleaks() {
    // arrange
    foo foo = new foo();
    leakdetector leakdetector = new leakdetector(foo);

    // act
    foo = null;

    // asserts
    leakdetector.assertmemoryleaksnotexist();
}

@test
public void testwithleak() {
    // arrange
    foo foo = new foo();
    foo bar = foo;
    leakdetector leakdetector = new leakdetector(foo);

    // act
    foo = null;

    // asserts
    leakdetector.assertmemoryleaksexist();
}


let’s take a look at the code of the memory leaks detector:

public class leakdetector extends phantomreference<object> {

    private final string description;

    /**
     * initialization of the memory leaks detector.
     * @param referent the object(resource) for which we are looking for leaks.
     */
    public leakdetector(object referent) {
        super(referent, new referencequeue<>());
        this.description = string.valueof(referent);
    }

    /**
     * if exists memory leaks then throws a fail.
     *
     * warn: run this method after delete all references on the checkable object(resource)
     */
    public void assertmemoryleaksnotexist() {
        gcutils.fullfinalization();

        assertions.assertthat(isenqueued())
                  .as("object: " + description + " was leaked")
                  .istrue();
    }

    /**
     * if not exists memory leaks then throws a fail.
     *
     * warn: run this method after delete all references on the checkable object(resource)
     */
    public void assertmemoryleaksexist() {
        gcutils.fullfinalization();

        assertions.assertthat(isenqueued())
                  .as("object: " + description + " already collected by the gc")
                  .isfalse();
    }
}


references

the source code of this project is available on github .

garbage collection Testing Garbage (computer science) Memory (storage engine) Object (computer science)

Published at DZone with permission of Anatoliy Korovin. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • All You Need To Know About Garbage Collection in Java
  • Java Memory Management
  • Choosing the Best Garbage Collection Algorithm for Better Performance in Java
  • Java Z Garbage Collector (ZGC): Revolutionizing Memory Management

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends: