AppPerfect Java Profiler 


Tutorial : Memory Leak Detection

This tutorial provides instructions on how to use AppPerfect Java Profiler product for detecting memory leak in a java application. This tutorial assumes you have successfully downloaded and installed AppPerfect DevTest4J on your machine with the default options.

This document is divided into following sections

  1. Creating Common Project
  2. AppPerfect Java Profiler

Within each section, multiple exercises are listed. All exercises assume you have installed the product in C:\AppPerfect\DevTest folder and will be referred as DEVTEST_HOME henceforth in tutorial. If you have installed the product in some other folder, modify the instructions below appropriately.

For this tutorial, create a java file called 'MemoryLeak.java' copy-pasting the code shown below and compile it.


import java.io.*;

public class MemoryLeak
{
    private static final int CAPACITY = 50;
    private static final int HALF_CAPACITY = CAPACITY/2;

    public static void main(String[] args) throws IOException
    {
        System.out.print("Press any key to start memory leak scenario... ");
        InputStreamReader reader = new InputStreamReader(System.in);
        reader.read();
        System.out.println();
        Stack stack = new Stack(CAPACITY);
        for (int i = 0; i < CAPACITY; ++i)
        {
            stack.push(new Object());
        }
        for (int i = 0; i < HALF_CAPACITY; ++i)
        {
            stack.pop();
        }
        System.out.print("Press any key to stop memory leak scenario... ");
        reader.read();
        reader.read();
    }
}

class Stack
{
    private Object[] elements;
    private int size = 0;
    public Stack(int initialCapacity)
    {
        this.elements = new Object[initialCapacity];
    }

    public void push(Object e)
    {
        ensureCapacity();
        elements[size++] = e;
    }

    public Object pop()
    {
        if (size > 0)
        {
            return elements[--size]; // MEMORY LEAK
        }
        return null;
    }

    /**
    * Ensure space for at least one more element, roughly
    * doubling the capacity each time the array needs to grow.
    */
    private void ensureCapacity()
    {
        if (elements.length == size)
        {
            Object[] oldElements = elements;
            elements = new Object[2 * elements.length + 1];
            System.arraycopy(oldElements, 0, elements, 0, size);
        }
    }
}

If a stack grows and then shrinks, the objects that were popped off the stack will not be garbage collected, even if the program using the stack has no more references to them. This is because the stack maintains obsolete references to these objects. An obsolete reference is simply a reference that will never be dereferenced again. In this case, any references outside of the “active portion” of the element array are obsolete. The active portion consists of the elements whose index is less than size.

- Source: Effective Java: Programming Language Guide by Joshua Bloch


Creating Common Project

Exercise 1: Launch AppPerfect DevTest4J

Action:

  1. Click on Start -> Programs ->AppPerfect DevTest4J x.x.x -> AppPerfect DevTest4J
  2. On launching AppPerfect DevTest4J a Welcome page will be displayed. Go through the brief description given for each product and click on the product icon to view its perspective.
    NB: Welcome page is displayed only when DevTest4J x.x.x is launched and last time no project was opened.
  3. To switch between different product perspectives click on corresponding project button in toolsbar.

Exercise 2: Creating a Common Project

Action:

  1. Launch the Common Project Wizard by clicking File ->New... menu option. The New Project wizard will be launched.
  2. Go through the instruction provided on top of the General tab.
  3. Keep the default project name and location for the purpose of this exercise. We don't have to provide "Notification" settings or "Remote Application/AppServer" settings for this exercise. Click on the Next button.
  4. In the Source tab provide the location of source file "MemoryLeak.java". Click the Next button.
  5. Use the default JDK which is bundled with AppPerfect DevTest4J and click on the Next button.
  6. In the "Environment" tab provide the location of "MemoryLeak.class" file.
  7. Click on "Verify Classpath" button to validate the classpath.
  8. Classpath validation dialog will be launched and the classpath will be verified. A message saying that the classpath specified is correct should be displayed. Click on the "OK" button. Click on the Next button.
  9. In the "Target" tab select project type as "LOCAL".
  10. For working folder provide the path to the folder containing the the "MemoryLeak.class" file.
  11. Specify the Main Class as "MemoryLeak".
  12. Select the "Launch target application automatically" checkbox. Click on the Finish button.
  13. A confirmation message saying that the project is saved will be displayed. Click on the OK button.

AppPerfect Java Profiler

NB:Please follow the steps provided in the "Creating Common Project" section to first create a common project, then proceed further.

Exercise 1: Define a Java Profiler project

Action:

  1. Once the common project is successfully created another dialog - Define Project Properties dialog - will be displayed.
  2. Read the instructions at top of each tab.
  3. Go through the descriptions for Profiling Types. Keep the default Development Mode Profiling and select Profiling Options tab.
  4. Study the descriptions of the three profiling options. You can configure Filters using Configure Filters option.Use default values.
  5. Study the descriptions of Instrumentation Options. Use the default: Dynamic Instrumentation enabled.
  6. Study the changes indicated in the Launch Instructions tab. AppPerfect Java Profiler must modify your application server's startup file to add instructions to load the profiling agent, etc. This tab shows the exact changes that needs to be made to the startup script. AppPerfect Java Profiler understands how to configure startup scripts for most commonly available application servers. Also, since it does not modify the original file and instead makes a copy to store the modifications, it is almost always desirable to let AppPerfect Java Profiler make the changes. Click OK button.
  7. Click through all the menu items to familiarize yourself with the available features and how to access them.
  8. Click on Tools ->Options... menu item. Click on the JDKs tab and ensure that the JDK path has been set correctly. This is the path provided for JDK during installation of AppPerfect DevTest4J. You may modify the path or add new JDK through this dialog box. It is critical that a correct version of JDK is available for AppPerfect DevTest4J to perform correctly.
  9. Click Help -> Table of Contents menu item to see AppPerfect DevTest4J product documentation.

Exercise 2: Start Profiling

Action:

  1. To start profiling click on Project -> Run from the menubar.
  2. A progress message will be shown while target application is launched.
  3. Observe the dynamic updation of data in the default (Summary) view. You can see various profiling metrics such as heap memory usage, object instance count and Thread count.
  4. From the Navigational Tree select Memory Leak Scenarios node.
  5. In Memory Leak Scenarios view, click on 'Start Scenario' button.
  6. Click on the 'Console' tab below and press any key. (Ensure not to press it often or else profiling will stop).
  7. Once the message 'Press any key to stop memory leak scenario...' is displayed, stop the scenario.
  8. Click Yes for heap snapshot.
  9. 'Snapshot Settings' dialog will come up. Leave default settings and Click Ok.
  10. Again, under 'Profiling' tab, select 'Memory Leak Scenarios'.
  11. Click on 'Scenarios' tab in 'Memory Leak Scenarios'.
  12. You will see one scenario in the Memory Leak Scenarios table called Scenario_1.
  13. Double click on 'Scenario_1'. You will be taken to the Packages/Classes tab.
  14. Here expand the java.lang node in the tree and you will see for Object, 50 objects are present in memory.
  15. This is the memory leak because if you see our java application, we are pushing 50 objects into the stack but then popping 25 objects. However the 25 object popped have not been garbage collected as a result of which there are still 50 objects of type 'Object' in memory.
  16. Stop Profiling by selecting Project -> Stop.
  17. Click Yes to stop profiling.
  18. Click No to collect information from the target application.
  19. Now open MemoryLeak.java and replace the 'pop()' method with the following:
    		public Object pop()
    		{
    			if (size > 0)
    			{
    				Object result = elements[--size];
    				elements[size] = null; // Eliminate obsolete reference
    				return result;
    			}
    			return null;
    		}
    		

  20. Recompile MemoryLeak.java.
  21. Start profiling again by selecting Project -> Run.
  22. Under 'Profiling' tab, select 'Memory Leak Scenarios'.
  23. Click on 'Start Scenario'.
  24. Click on the 'Console' tab below and press any key. (Ensure not to press it often or else profiling will stop).
  25. Once the message 'Press any key to stop memory leak scenario...' is displayed, stop the scenario.
  26. Click Yes for heap snapshot.
  27. 'Snapshot Settings' dialog will come up. Leave default settings and Click Ok.
  28. Again, under 'Profiling' tab, select 'Memory Leak Scenarios'.
  29. Click on 'Scenarios' tab in 'Memory Leak Scenarios'.
  30. You will see one scenario in the Memory Leak Scenarios table called Scenario_1.
  31. Double click on 'Scenario_1'. You will be taken to the Packages/Classes tab.
  32. Here expand the java.lang node in the tree and you will see for Object, 25 objects are present in memory.
  33. The 25 objects being popped have been garbage collected. There is no longer a memory leak.
  34. Stop Profiling by selecting Project -> Stop.
  35. Click Yes to stop profiling.
  36. Click No to collect information from the target application.