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
  • Performance Engineering Management: A Quick Guide
  • Java Memory Management
  • Heap Memory In Java Applications Performance Testing

Trending

  • Scalable System Design: Core Concepts for Building Reliable Software
  • Google Cloud Document AI Basics
  • Emerging Data Architectures: The Future of Data Management
  • *You* Can Shape Trend Reports: Join DZone's Software Supply Chain Security Research
  1. DZone
  2. Coding
  3. Languages
  4. Memory Leaks and Java Code

Memory Leaks and Java Code

When you aren't using objects, but they aren't touched by GC, a memory leak happens. Here are six ways memory leaks happen to look for and avoid.

By 
Shamik Mitra user avatar
Shamik Mitra
·
Aug. 29, 16 · Tutorial
Likes (43)
Comment
Save
Tweet
Share
68.9K Views

Join the DZone community and get the full member experience.

Join For Free

Java implicitly reclaims memory by GC (a daemon thread). GC periodically checks if there is any object which is unreachable or, to be precise, has no reference pointing to that object. If so, GC reclaims the newly-available memory.

Now the question is should we worry about memory leaks or how Java handles it?

Pay attention to the definition: An object is eligible for garbage collection when it is unreachable (unused), and no living thread can reach it.

So if an object which is not used in an application but unintentionally has references, it is not eligible for garbage collection, and is a potential memory leak.

GC takes care of unreachable objects, but can’t determine unused objects. Unused objects depend on application logic, so a programmer must pay attention to the business code. Silly mistakes silently grow up to be a monster.

Memory leaks can occur in many ways, I will look at some examples.

Example 1: Autoboxing

package com.example.memoryleak;
public class Adder {
       publiclong addIncremental(long l)
       {
              Long sum=0L;
               sum =sum+l;
               return sum;
       }
       public static void main(String[] args) {
              Adder adder = new Adder();
              for(long ;i<1000;i++)
              {
                     adder.addIncremental(i);
              }
       }
}

Can you spot the memory leak?

Here I made a mistake. Instead of taking the primitive long for the sum, I took the Long (wrapper class), which is the cause of the memory leak. Due to auto-boxing, sum=sum+l; creates a new object in every iteration, so 1000 unnecessary objects will be created. Please avoid mixing and matching between primitive and wrapper classes. Try to use primitive as much as you can.

Example 2: Using Cache

package com.example.memoryleak;
import java.util.HashMap;
import java.util.Map;
public class Cache {
       private Map<String,String> map= new HashMap<String,String>();
       publicvoid initCache()
       {
              map.put("Anil", "Work as Engineer");
              map.put("Shamik", "Work as Java Engineer");
              map.put("Ram", "Work as Doctor");
       }
       public Map<String,String> getCache()
       {
              return map;
       }
       publicvoid forEachDisplay()
       {
              for(String key : map.keySet())
              {
                String val = map.get(key);                 
                System.out.println(key + " :: "+ val);
              }
       }
       public static void main(String[] args) {            
              Cache cache = new Cache();
              cache.initCache();
              cache.forEachDisplay();
       }
}

Here, a memory leak occurs due to the internal map data structure. This class is to display the employee value from the cache. Once those are displayed, there is no need to store those elements in the cache.

We forgot to clear the cache, so although objects in cache are not required anymore by the application, it can’t be GCed, as map holds a strong reference to them.

So when you're using your own Cache, don’t forget to clear them if items in the cache are no longer required. Alternatively, you can initialize cache by WeakHashMap. The beauty of WeakHashMap is, if keys are not referenced by any other objects, then that entry will be eligible for GC.

There is lot to say about WeakHashMap, but I will discuss it in another article. Use it with caution, if you want to reuse the values stored in the cache, it may be that its key is not referenced by any other object, so the entry will be GCed and that value magically disappears.

Example 3: Closing Connections

try
{
  Connection con = DriverManager.getConnection();
  …………………..
    con.close();
}

Catch(exception ex)
{
}

In the above example, we close the connection (Costly) resource in the try block, so in the case of an exception, the connection will not be closed. So it creates a memory leak as this connection never return back to the pool.

Please always put any closing stuff in the finally block.

Example 4: Using CustomKey

package com.example.memoryleak;
import java.util.HashMap;
import java.util.Map;
public class CustomKey {
       public CustomKey(String name)
       {
              this.name=name;
       }
       private String name;
       publicstaticvoid main(String[] args) {
              Map<CustomKey,String> map = new HashMap<CustomKey,String>();
              map.put(new CustomKey("Shamik"), "Shamik Mitra");
              String val = map.get(new CustomKey("Shamik"));
              System.out.println("Missing equals and hascode so value is not accessible from Map " + val);
       }
}

As in CustomKey we forgot to provide equals() and hashcode() implementation, so a key and value stored in map can’t be retrieved later, as the map get() method checks hashcode() and equals(). But this entry is not able to be GCed, as the map has a reference to it, but application can’t access it. Definitely a memory leak.

So when you make your Custom key, always provide an equals and hashcode() implementation.

Example 5: Mutable Custom Key

package com.example.memoryleak;
import java.util.HashMap;
import java.util.Map;
public class MutableCustomKey {
       public MutableCustomKey(String name)
       {
              this.name=name;
       }
       private String name;
       public String getName() {
              return name;
       }
       publicvoid setName(String name) {
              this.name = name;
       }
       @Override
       public int hashCode() {
              final int prime = 31;
              int result = 1;
              result = prime * result + ((name == null) ? 0 : name.hashCode());
              return result;
       }
       @Override
       public boolean equals(Object obj) {
              if (this == obj)
                     return true;
              if (obj == null)
                     return false;
              if (getClass() != obj.getClass())
                     return false;
              MutableCustomKey other = (MutableCustomKey) obj;
              if (name == null) {
                     if (other.name != null)
                           return  false;
              } elseif (!name.equals(other.name))
                     return false;
              return true;
       }
       public static void main(String[] args) {
              MutableCustomKey key = new MutableCustomKey("Shamik");             
              Map<MutableCustomKey,String> map = new HashMap<MutableCustomKey,String>();
              map.put(key, "Shamik Mitra");
              MutableCustomKey refKey = new MutableCustomKey("Shamik");
              String val = map.get(refKey);
              System.out.println("Value Found " + val);
              key.setName("Bubun");
              String val1 = map.get(refKey);
              System.out.println("Due to MutableKey value not found " + val1);
       }
}

Although here we provided equals() and hashcode() for the custom Key, we made it mutable unintentionally after storing it into the map. If its property is changed, then that entry will never be found by the application, but map holds a reference, so a memory leak happens.

Always make your custom key immutable.

Example 6: Internal Data Structure

package com.example.memoryleak;
public class Stack {
       privateint maxSize;
       privateint[] stackArray;
       privateint pointer;
       public Stack(int s) {
              maxSize = s;
              stackArray = newint[maxSize];
              pointer = -1;
       }
       public void push(int j) {
              stackArray[++pointer] = j;
       }
       public int pop() {
              return stackArray[pointer--];
       }
       public int peek() {
              return stackArray[pointer];
       }
       publicboolean isEmpty() {
              return (pointer == -1);
       }
       public boolean isFull() {
              return (pointer == maxSize - 1);
       }
       public static void main(String[] args) {
              Stack stack = new Stack(1000);
              for(int ;i<1000;i++)
              {
                     stack.push(i);
              }
              for(int ;i<1000;i++)
              {
                     int element = stack.pop();
                     System.out.println("Poped element is "+ element);
              }
       }
}

Here we face a tricky problem when Stack first grows then shrinks. Actually, it is due to the internal implementation. Stack internally holds an array, but from an application perspective, the active portion of Stack is where the pointer is pointing.

So when Stack grows to 1000, internally the array cells are filled up with elements, but afterwards when we pop all elements, the pointer comes to zero, so according to the application it is empty, but the internal array contains all popped references. In Java, we call it an obsolete reference. An obsolete reference is a reference which can’t be dereferenced.

This reference can’t be GCed, as the array holds those elements, but they are unnecessary after they are popped.

To fix it, we need to set the null value when the pop action occurs so those objects are able to be GCed.

public int pop() {
              int size = pointer--
              int element= stackArray[size];
              stackArray[size];
              return element;
       }

Safety Measure for Preventing Memory Leaks:

Image title

Memory (storage engine) Java (programming language) garbage collection Data structure Object (computer science) application Cache (computing)

Published at DZone with permission of Shamik Mitra, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • All You Need To Know About Garbage Collection in Java
  • Performance Engineering Management: A Quick Guide
  • Java Memory Management
  • Heap Memory In Java Applications Performance Testing

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: