JProfiler is a Java profiler tool that is useful when troubleshooting memory-related issues in Ignition. It combines time, memory, and thread profilers in a single application to measure CPU and memory allocation. This highlights inefficiencies, large memory usage, or memory leak origins in a simple way to help users identify performance bottlenecks and resolve threading problems.
JProfiler profiling is supported on Windows, Linux, Solaris, AIX, FREEBSD, and HP-UX platforms, and it is integrated through IDEs like Eclipse and IntelliJ. JProfiler is typically used locally, but with certain adjustments profiling can be done remotely.
Taking an in-depth look at memory is a great way to solve performance issues, especially when there doesn't seem to be a pattern or obvious explanation to what is causing your memory spikes. Using JProfiler can help you understand how your memory is being used to determine the source of a problem, if a sub-system isn’t performing as expected, if there is a memory leak, or if the state of the system isn’t what you’d expect.
A good starting practice is to analyze your system to assess potential problem areas during development so you can adapt design as you build, but profilers can also be hugely beneficial while your system is actively running as a quick way to solve both simple and complex memory problems. Specifically, JProfiler’s Heap Walker View allows browsing to understand what objects are in use in your system. The Heap Walker needs memory heap dumps, which are snapshots of the current state of a system that shows root objects and their references to live objects. If you need any assistance creating a memory heap, refer to the Generating Java Memory Dumps Knowledge Base Article. However, if you have JProfiler installed on a machine that can connect to the Gateway, JProfiler can be used instead to capture the heap dump while it is live profiling a JVM, which can be more convenient than using JMap. To do this, use the Attach to a Running JVM option on the JProfiler Quick Start menu.
Opening a memory heap dump in JProfiler will show if the state of the system matches expectations. You can see what kind of objects you have a lot of, what are the biggest objects, and even compare heap dumps to see what differences exist at various times of capture. Note, it is difficult to determine a memory leak with a heap dump, but the available information may be able to point to where a problem is originating.
To download and install JProfiler:
- Select the download you need from their website Downloads page.
- Open the JProfiler Setup Wizard, select the top option, Install into:, and select Next.
- Accept the License Agreement and select Next.
- Confirm the License Information option you want. Select Next.
- Use the dropdown to select the target IDE to integrate JProfiler.
- Follow the Install plugin instructions to integrate. This example uses IntelliJ IDEA as the target IDE.
- Once complete, select if you want to Run JProfiler immediately and click Finish.
JProfiler organizes profiling data into View sections. View options are listed tabs on the left side of the main window. Each section contains inspection options that highlight different aspects of the current set of objects. View options include Telemetry, Live Memory, Heap Walker, CPU Views, Threads, Monitors, Databases, Probes, and MBeans.
Heap Walker Snapshot
A Quick Start popup appears when you open JProfiler and offers a few shortcut profiling options. If this is your first time using JProfiler, you can use the demo session to explore other JProfiler features. The following instructions explore the Heap Walker views. In JProfiler's Heap Walker you can open a memory heap dump of your system as a snapshot to drill down to objects of interest by performing selection steps.
To open a snapshot, click Open a snapshot from the Quick Start popup or select Session on the menu bar and choose Open Snapshot. Then, locate and select the memory dump file you created. Ensure that your memory dump was created using one of JProfiler’s supported formats: JProfiler (.jps), HPROF (.hprof, .bin), Compressed HPROF (.hpz, .hprof.gz), IBM PHD (.phd), or JDK flight recording (.jfr). Depending on the size of your memory dump, and if a file transfer to another computer is required, consider compressing to a zip file before transfer to cut down dump size.
You may also notice an analysis folder will be created in your memory heap location. This indicates it’s been opened in JProfiler and now can be opened from the Session > Recent Snapshots dropdown list in the future.
After opening a Snapshot, the main window displays the analysis type options, object configuration settings, and a list of all available classes, many of which are from libraries and frameworks that Ignition relies on.
You can select through these analysis type options to change your view, but you'll notice some views require you to make selections before they will properly populate. To do this, you’ll need to find an object you are interested in on the Classes screen and double-click. This will open a New Object Set popup that allows you to choose a view for the object set. For example, if you choose References, select Outgoing references from the dropdown, and select OK, it will take you directly to the References tab that is now populated with your chosen object set.
New icons are now present in the list of objects displayed that indicate you are viewing reference trees for each instance. Click on the single instances to expand and to trace to the level you need. Use the Back and Forward Arrows on the top panel for an easy way to return to a View you navigated away from.
The default analysis type that opens is Classes, but the remaining options include Allocations, Biggest Objects, References, Time, Inspections, and Graph.
- Classes: Shows all classes and their instances.
- Allocations: Shows an allocation tree and allocation hot spots for recorded objects.
- Biggest objects: Shows objects that block the heap’s largest parts from being cleared. The dominator tree can be expanded to show retained objects.
- References: Shows outgoing and incoming references, as well as the ability to show combined views for incoming and outgoing references. In the outgoing references view, you can apply filters to either sort by primitive values or with a script.
- Time: Shows a time-resolved histogram of recorded objects.
- Inspections: This analysis type runs a number of inspections on the current object set to analyze your objects in various ways.
- Graph: You can add objects here from different object sets to explore reference relationships and find paths between selected objects or to garbage collector roots.
Object configuration settings
Directly above the object list are the object configuration settings. These will vary slightly depending on the view you are currently using, but they all serve the same purpose of allowing you to adjust how the listed objects are sorted or displayed. For example, in the Biggest Objects view second dropdown, you can change the view display from a Tree to a Sunburst Diagram, which will highlight the object arrays and sizes in a way that may be easier to analyze, especially when selected to expand certain instances.
Additionally, these settings allow you to continue changing your View display even after your initial setup. On the References View for example, you can use the first dropdown to change from Outgoing references to Incoming references. You’ll also see a few Merge reference options in this list. These are options to further condense your search results on incoming, outgoing, or dominant references.
Main Window Columns
In a similar way to the object configuration settings, the list columns will change depending on what is relevant for the corresponding view. For example, the Classes view includes Name, Instance Count, Size, and Retained Size columns. However, opening the References view will instead have Object, Retained Size, Shallow Size, and Allocation Time columns. Note, if you don’t automatically see the Retained Sizes column, click the Calculate estimated retained sizes link.
Understanding how Ignition’s structure works is essential for knowing what to search for in JProfiler. For example, if you were looking into a problematic tag history query, you would need to know that tag history queries occur between the client and the Ignition Gateway, and once they hit the Gateway, something has to process them. So you would start by researching classes relevant to tag history while searching the threads in the memory heap dump for packages that those classes would be held in.
Being methodical with how you analyze your system for suspicious objects can bring to light inefficiencies that may have been previously overlooked, such as start/end ranges that aren’t set up optimally or set to allow adjustments on the fly that may conflict with how the system was designed to run. Using this information, you could institute better strategies or find areas where more limits can be placed to prevent future memory spikes and unresponsive Gateways.
Under the main window on the Heap Walker Classes view, you can type keywords into the filter field on the bottom to narrow down the displayed objects before moving into deeper investigation. For instance, entering “com.inductiveautomation” into the filter field will only show Ignition specific code objects.
If you aren’t sure where to start looking in your snapshot, consider some of these common areas that may lead to finding the source of your memory issues:
- Instance Count: A particular object set may show signs of a problem simply by looking at the number of instances that were recorded during the snapshot. Take note of any object sets that may be exceeding the amount of typical or expected usage.
- Check outgoing references of suspicious object sets: Creating views for outgoing references of an object set will allow you to browse through the properties of each of those instantiated objects in a tree format. This may give you insight on what to expect from these types of instances in terms of structure.
- Check incoming references of suspicious object sets: Creating views for incoming references of an object set will allow you to browse through a tree-formatted list of other objects looking at it. This may give you an idea of the expected relationship between a selected object set instance and different objects related to it.
- Retained size: Retained size is the size of an object, including its primitives and pointers, and all connected or reaching objects. Looking here can show where memory is being allocated and determine if the object sizes match expectations.
- Biggest objects: Looking deeper into the Biggest objects is another way to verify if object size matches expectations. Remember, total memory is an important factor to consider, but it can be dangerous to assume it is a problem. If your issue is a memory leak, looking through the biggest objects may not be your focus, but a suspiciously large retained size in a single object or a build-up of classes (if you group by class) could both hint at a potential memory leak.
- Show Path to GC Root: This option in the Graph view eliminates needing to manually reveal incoming references by bringing you directly to the root GC.
If your memory concerns are related to how your threads are executing, CPU Views or Thread Views may be helpful places to start. CPU Views can be aggregated on a method, class, package, or component level and users can record call trees to optimize performance. Threads, thread groups, and thread statuses can be chosen for all CPU Views as well.
Additionally, Thread profiling includes thread history, thread monitor, and thread dump views. These each offer different ways to analyze thread allocation. Thread history displays a thread activity and status timeline. Thread monitor lists all live threads and their related activity. Thread dumps analyzes multiple thread dumps in a single view.
For more information on JProfiler, click here to read the Definitive Guide.