My Oracle Support Banner

OutOfMemoryError and Memory Leak Debugging Methodology (Doc ID 1054263.1)

Last updated on SEPTEMBER 25, 2020

Applies to:

Oracle WebLogic Server - Version 8.1 to 10.3
Information in this document applies to any platform.
Japanese Version of Article: OutOfMemoryError とメモリリークに関する調査のメソドロジー [ID 1544030.1]

Purpose

Scope

This document will explain the methodology of debugging a "java.lang.OutOfMemoryError" occurring in the java heap. It does not discuss the debugging methodology when the OutOfMemoryError occurs in the native heap. Debugging methodology will be demonstrated using some examples and tools. This is a methodology only and the tools that can be used in actual scenarios could be different based on availability in customer environment. However, the methodology does not change irrespective of the tool being used. This methodology can be used for any JVM (vendor independent) and on any platform/OS (vendor independent).

Introduction

  1. What does the error "java.lang.OutOfMemoryError" mean in regards to the java heap?

    When an application displays Out of Memory errors, it means that the memory/heap on the Java Virtual Machine (JVM) on which the application is running has been exhausted.
     
  2. What could be the causes of the Out Of Memory (OOM) error OR memory exhaustion?

    At a high level the causes of Out Of Memory in Java Heap can be classified into two categories:
        a. Genuine memory exhaustion due to overload on the JVM (more application requests than the allocated heap can handle).
        b. Memory leak in the java heap.
     
  3. What is a memory leak in the java heap?

    Constant memory growth in the java heap, in spite of regular Garbage Collection (GC) occurrences.

    To further explain this at a low-level:
    NOTE:  The attribute values used represent fictitious sample names that made up as examples.  Any similarity to actual code, is purely coincidental and not intended in any other manner.

    If a program holds a reference to a heap chunk that is not used during the rest of its life, it is considered a memory leak because the memory could have been freed and reused. GC won't reclaim it due to the reference being held by the program. A Java program could run out of memory due to such leaks. Java memory leaks are mostly a result of non-obvious programming errors. A simple example is shown below:
    import java.io.IOException;
    import java.util.HashSet;
    import java.util.Random;
    import java.util.Vector;

    public class LeakExample {
    static Vector myVector = new Vector();
    static HashSet pendingRequests = new HashSet();

    public void slowlyLeakingVector(int iter, int count) {
      for (int i=0; i<iter; i++) {
        for (int n=0; n<count; n++) {
          myVector.add(Integer.toString(n+i));
        }
        for (int n=count-1; n>0; n--) {
        // Oops, it should be n>=0
        myVector.removeElementAt(n);
        }
      }
    }

    public void leakingRequestLog(int iter) {
      Random requestQueue = new Random();
      for (int i=0; i<iter; i++) {
        int newRequest = requestQueue.nextInt();
        pendingRequests.add(new Integer(newRequest));
        // processed request, but forgot to remove it
        // from pending requests
      }
    }

    public void noLeak(int size) {
      HashSet tmpStore = new HashSet();
      for (int i=0; i<size; ++i) {
        String leakingUnit = new String("Object: " + i);
        tmpStore.add(leakingUnit);
      }
      // Though highest memory allocation happens in this
      // function, but all these objects get garbage
      // collected at the end of this method, so no leak.
    }

    public static void main(String[] args) throws IOException {
      LeakExample javaLeaks = new LeakExample();
      for (int i=0; true; i++) {
        try { // sleep to slow down leaking process
          Thread.sleep(1000);
        } catch (InterruptedException e) { /* do nothing */ }
          System.out.println("Iteration: " + i);
          javaLeaks.slowlyLeakingVector(1000,10);
          javaLeaks.leakingRequestLog(5000);
          javaLeaks.noLeak(100000);
      }
    }
    }

    The LeakExample class has three methods: slowlyLeakingVector, leakingRequestLog, and noLeak. The slowlyLeakingVector has a wrong condition in the second for loop, due to which one String object leaks in every iteration. Instead of String, if it was a database record with significant size, this slow leak can result in a non-responding application running out of memory fairly quickly. The leakingRequestLog represents a very common case where an incoming request is kept in a hash table till it is completed. Except in this example, the programmer has forgotten to remove it from the hash table once the request is done. Over a period of time, the hash table will have plenty of entries, which will result in hash-clashes as well as a large portion of heap occupied by useless hash entries. Both of these are very common cases resulting into Java memory leaks.

    Instinctively, you may think that the locations of highest memory allocation result in leaks. However, this may not be true. For example, the noLeak method allocates a lot of memory, but all of it gets garbage collected. On the other hand, the other two methods don't allocate that much memory, but are causing memory leaks.

Troubleshooting Steps

To view full details, sign in with your My Oracle Support account.

Don't have a My Oracle Support account? Click to get started!


In this Document
Purpose
 Scope
 Introduction
Troubleshooting Steps
 Cause Determination for OOM
 Debugging memory leaks
References

My Oracle Support provides customers with access to over a million knowledge articles and a vibrant support community of peers and Oracle experts.