Enterprise Java

Using the NetBeans Profiler Programmatically in Java

This article explores how to use the NetBeans Profiler API to collect heap dumps, analyze memory usage, and inspect specific class instances programmatically within a Java application. We will explore using the NetBeans Profiler API to analyze performance metrics in Java applications programmatically. Rather than relying on VisualVM or GUI-based tools, programmatic profiling provides a flexible way to collect data from running applications and output it directly to the console or logs.

1. Introduction to NetBeans Profiler API

The NetBeans Profiler is an effective tool for identifying performance bottlenecks, memory leaks, and usage patterns in Java applications. While its graphical interface is useful for in-depth profiling, accessing the profiler programmatically offers a streamlined, automated approach. The NetBeans Profiler API, available in the open-source NetBeans repository, enables programmatic access to profiling tools within the NetBeans IDE, allowing us to capture essential performance data directly in our code.

2. Setting up a Sample Project

To begin, a simple Maven project should be set up, including the NetBeans Profiler API. If the profiler is not directly available, the NetBeans repository may need to be added. Here is an example pom.xml configuration:

    <dependencies>
        <!-- NetBeans Profiler dependency -->
        <dependency>
            <groupId>org.netbeans.modules</groupId>
            <artifactId>org-netbeans-lib-profiler</artifactId>
            <version>REPLACE_WITH_VERSION</version>
        </dependency>
    </dependencies>
    <repositories>
        <repository>
            <id>netbeans</id>
            <name>netbeans</name>
            <url>http://bits.netbeans.org/maven2</url>
        </repository>
    </repositories>

Replace REPLACE_WITH_VERSION with the appropriate version for the NetBeans Profiler API.

3. Using the NetBeans Profiler API Programmatically

We will create a simple Java project that performs computational tasks and allocates memory, enabling us to analyze its performance using the NetBeans Profiler API. Let’s create a Library management system as an example, where each Library has books, sections, and other attributes. This class will simulate memory usage and provide data we can inspect in a heap dump.

public class Library {

    private int id;
    private String name;
    private List<String> books = new ArrayList<>();
    private List<String> sections = new ArrayList<>();

    public Library(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public void addBook(String bookName) {
        books.add(bookName);
    }

    public void addSection(String sectionName) {
        sections.add(sectionName);
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<String> getBooks() {
        return books;
    }

    public void setBooks(List<String> books) {
        this.books = books;
    }

    public List<String> getSections() {
        return sections;
    }

    public void setSections(List<String> sections) {
        this.sections = sections;
    } 
}

In this example:

  • Library has attributes like id, name, books, and sections.
  • The addBook and addSection methods allow adding books and sections to the library.

4. Taking a Heap Dump

To capture heap dumps programmatically, we will use a utility class named HeapDumpUtility. This class leverages the HotSpotDiagnosticMXBean from the com.sun.management package to trigger a heap dump at a specific point in the application.

import java.io.IOException;
import java.lang.management.ManagementFactory;
import com.sun.management.HotSpotDiagnosticMXBean;

public class HeapDumpUtility {

    private static final String HOTSPOT_BEAN_NAME = "com.sun.management:type=HotSpotDiagnostic";
    private static volatile HotSpotDiagnosticMXBean hotspotMBean;

    public static void dumpHeap(String filename, boolean live) throws IOException {
        initHotspotMBean();
        hotspotMBean.dumpHeap(filename, live);
    }

    private static void initHotspotMBean() throws IOException {
        if (hotspotMBean == null) {
            synchronized (HeapDumpUtility.class) {
                hotspotMBean = ManagementFactory.newPlatformMXBeanProxy(
                        ManagementFactory.getPlatformMBeanServer(),
                        HOTSPOT_BEAN_NAME,
                        HotSpotDiagnosticMXBean.class);
            }
        }
    }
}

This HeapDumperUtility class uses the HotSpotDiagnosticMXBean to dump the heap into a file.

5. Analyzing the Heap Dump

Once the heap dump is created, we can load it into VisualVM or the NetBeans Profiler’s GUI to examine memory usage. In this example, however, we utilize NetBeans’ built-in in-code APIs to analyze heap dumps directly through code. Below is an example of how to load and analyze a heap dump programmatically using the NetBeans API:

import java.io.File;
import java.io.IOException;
import org.netbeans.lib.profiler.heap.HeapSummary;
import org.netbeans.lib.profiler.heap.Heap;
import org.netbeans.lib.profiler.heap.HeapFactory;

public class ProfilingExample {

    private static Heap heap;

    private static void analyzeHeapDump() throws IOException {
        heap = HeapFactory.createHeap(new File("heapdump.hprof"));

        HeapSummary summary = heap.getSummary();

        System.out.println("Total number of instances: " + summary.getTotalLiveInstances());
        System.out.println("Memory usage in bytes: " + summary.getTotalLiveBytes());
        System.out.println("GC Roots: " + heap.getGCRoots().size());
        System.out.println("Total number of Classes: " + heap.getAllClasses().size());
        System.out.println("Timestamp: " + summary.getTime());

    }

    public static void main(String[] args) throws IOException {
        Library cityLibrary = new Library(1, "Programming Library");
        cityLibrary.addBook("Java Programming Essentials");
        cityLibrary.addBook("Data Structures and Algorithms");
        cityLibrary.addSection("Technology");
        cityLibrary.addSection("Literature");

        Library homeLibrary = new Library(1, "Philosophical Library");
        homeLibrary.addBook("Age of Reason");
        homeLibrary.addBook("The Principles of Nature");
        homeLibrary.addSection("Philosophy");

        File f = new File("heapdump.hprof");
        if (f.exists()) {
            f.delete();
        }

        HeapDumpUtility.dumpHeap("heapdump.hprof", true);

        analyzeHeapDump();

    }
}

In the main method, two Library instances (cityLibrary and homeLibrary) are created, each with sample data representing books and sections. After initializing these objects, the code calls HeapDumpUtility.dumpHeap() to generate a heap dump file, heapdump.hprof, which captures the current memory state, including object references and the structure of Library instances and related data. This setup simulates a basic memory allocation scenario in which objects are created and stored for later analysis.

Following the heap dump creation, the analyzeHeapDump method loads the heap dump file using the NetBeans Profiler API’s HeapFactory.createHeap() method. This method generates a Heap instance from the dump file, enabling programmatic analysis of the memory contents.

When the code runs, the output will display summary information about the heap dump, including details like the total number of instances, memory usage in bytes, GC roots, the total number of classes, and the timestamp of the dump. Below is an example output and a breakdown of each line:

output of example using java netbeans profiler programmatically

These insights provide a snapshot of the memory footprint of the running application at the time of the dump, offering a straightforward way to assess memory allocation and usage within the Library instances. Here is a summary of each output line:

  • Total instances: 31,679 live objects in memory
  • Memory usage: 1,836,066 bytes of memory allocated (approximately 1.84 MB).
  • GC Roots: 1,418 objects reachable by garbage collection
  • Total classes: 1,526 unique classes loaded in the heap
  • Timestamp: 1731436123085 (time of heap dump creation)

5.2 Analyzing Library Class Instances

In this section, we analyze the memory usage and number of instances for the Library class within the heap dump. The following code retrieves the Library class from the heap and calculates its memory consumption:

    public static void librarySummary() {
        JavaClass libraryClass = heap.getJavaClassByName("com.jcg.profilingexample.Library");
        if (libraryClass == null) {
            System.out.println("Library class not located");
            return;
        }
        List<Instance> instances = libraryClass.getInstances();
        long totalSize = 0;
        long instancesNumber = libraryClass.getInstancesCount();
        for (Instance instance : instances) {
            totalSize += instance.getSize();
        }
        System.out.println("Number of Library instances: " + instancesNumber);
        System.out.println("Memory consumed by Library instances: " + totalSize);
    }

This block of code starts by looking for the Library class in the heap using heap.getJavaClassByName("com.jcg.profilingexample.Library"). If the class is not found, it prints a message and exits the method. If the class is found, it retrieves all instances of the Library class using libraryClass.getInstances() and counts how many instances of the class exist in the heap with libraryClass.getInstancesCount().

Next, it calculates the total memory used by all instances of the Library class by summing the size of each instance. This is done by iterating over the list of instances and adding up the size of each one using instance.getSize().

Example output:

Number of Library instances: 2
Memory consumed by Library instances: 88

Explanation of Output:

  • Number of Library instances: 2 – This output indicates that there are a total of 2 instances of the Library class in memory. The getInstancesCount() method counts how many Library objects are present within the heap dump.
  • Memory consumed by Library instances: 88 – This shows the total memory usage (in bytes) by all Library instances in the heap. The getSize() method is used to sum up the memory taken up by each individual Library object, giving the total memory usage.

6. Conclusion

In this article, we explored how to use the NetBeans Profiler API to capture and analyze heap dumps in a Java application. We demonstrated how to create and analyze a heap dump to inspect the memory usage and number of instances of a specific class. By using the provided code, we were able to track the total number of Class instances and their memory consumption, providing valuable insights into the memory behaviour of our application.

7. Download the Source Code

This article covered how to use the Java NetBeans profiler programmatically.

Download
You can download the full source code of this example here: java netbeans use profiler programmatically

Omozegie Aziegbe

Omos Aziegbe is a technical writer and web/application developer with a BSc in Computer Science and Software Engineering from the University of Bedfordshire. Specializing in Java enterprise applications with the Jakarta EE framework, Omos also works with HTML5, CSS, and JavaScript for web development. As a freelance web developer, Omos combines technical expertise with research and writing on topics such as software engineering, programming, web application development, computer science, and technology.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button