1. 概述
性能分析能深入揭示应用运行时的行为。Java生态中有多种主流分析工具,如NetBeans Profiler、JProfiler和VisualVM。其中NetBeans Profiler提供了强大的免费功能。
本文将演示如何通过编程方式使用NetBeans Profiler API:创建示例项目、生成堆转储文件,并利用API进行分析。
2. NetBeans Profiler 简介
NetBeans IDE内置免费的分析器,可用于分析Java应用。它通过IDE中的直观界面提供CPU性能和内存使用分析功能。
但NetBeans Profiler也开放了其分析API供编程使用。这对自动化堆转储分析特别有用,可减少对GUI工具的依赖。
堆转储是应用在某个时间点的内存快照。它能有效反映内存使用情况,包含内存中的存活对象、类与字段信息,以及对象间的引用关系。
3. 示例项目搭建
要使用NetBeans Profiler API,需在pom.xml中添加依赖:
<dependency>
<groupId>org.netbeans.modules</groupId>
<artifactId>org-netbeans-lib-profiler</artifactId>
<version>RELEASE220</version>
</dependency>
此依赖提供JavaClasses
和Instances
等类,用于分析类、实例数量及内存占用。
接下来创建示例项目并分析其堆转储:
class SolarSystem {
private static final Logger LOGGER = Logger.getLogger(SolarSystem.class.getName());
private int id;
private String name;
private List<String> planet = new ArrayList<>();
// 构造方法省略
public void logSolarSystem() {
LOGGER.info(name);
LOGGER.info(String.valueOf(id));
LOGGER.info(planet.toString());
}
}
上述代码定义了SolarSystem
类,用于记录太阳系的名称、ID和行星信息。
应用运行时,可通过jmap
工具获取堆转储文件,也可编程方式生成:
static void dumpHeap(String filePath, boolean live) throws IOException {
MBeanServer server = ManagementFactory.getPlatformMBeanServer();
HotSpotDiagnosticMXBean mxBean = ManagementFactory.newPlatformMXBeanProxy(
server, "com.sun.management:type=HotSpotDiagnostic", HotSpotDiagnosticMXBean.class);
mxBean.dumpHeap(filePath, live);
}
此代码通过MBeanServer
和HotSpotDiagnosticMXBean
实现堆转储。
创建SolApp
类并实例化SolarSystem
:
class SolApp {
static void solarSystem() throws IOException {
List<String> planet = new ArrayList<>();
planet.add("Mercury");
planet.add("Mars");
planet.add("Earth");
planet.add("Venus");
SolarSystem solarSystem = new SolarSystem(1, "Sol System", planet);
solarSystem.logSolarSystem();
HeapDump.dumpHeap("solarSystem.hprof", true);
}
}
执行后,堆转储文件solarSystem.hprof
会生成到项目根目录。
通过以下代码加载堆转储文件:
Heap heap = HeapFactory.createHeap(new File("solarSystem.hprof"));
现在可调用Heap
对象的各种方法进行深入分析,包括:
- 检查类的内存占用
- 检测潜在内存泄漏
- 基于分析结果优化代码性能
4. 堆转储概览
首先获取堆转储的总体信息:
static void heapDumpSummary() {
HeapSummary summary = heap.getSummary();
LOGGER.info("Total instances: " + summary.getTotalLiveInstances());
LOGGER.info("Total bytes: " + summary.getTotalLiveBytes());
LOGGER.info("Time: " + summary.getTime());
LOGGER.info("GC Roots: " + heap.getGCRoots().size());
LOGGER.info("Total classes: " + heap.getAllClasses().size());
}
控制台输出如下:
INFO com.baeldung.netbeanprofiler.SolApp -- Total instances: 79893
INFO com.baeldung.netbeanprofiler.SolApp -- Total bytes: 6235526
INFO com.baeldung.netbeanprofiler.SolApp -- Time: 1724568603079
INFO com.baeldung.netbeanprofiler.SolApp -- GC Roots: 2612
INFO com.baeldung.netbeanprofiler.SolApp -- Total classes: 3207
结果显示:
- 存活对象总数:79,893个
- 总内存占用:约6MB
- 类总数:3,207个
- GC Roots数量:2,612个
这些存活对象由3,207个类的实例组成,占用约6MB内存。
5. 类直方图分析
获取堆转储概览后,进一步分析类的实例数量:
List<JavaClass> unmodifiableClasses = heap.getAllClasses();
List<JavaClass> classes = new ArrayList<>(unmodifiableClasses);
classes.sort((c1, c2) -> Long.compare(c2.getInstancesCount(), c1.getInstancesCount()));
按实例数量排序后,输出前5名:
for (int i = 0; i < Math.min(5, classes.size()); i++) {
JavaClass javaClass = classes.get(i);
LOGGER.info(" " + javaClass.getName());
LOGGER.info(" Instances: " + javaClass.getInstancesCount());
LOGGER.info(" Total size: " + javaClass.getAllInstancesSize() + " bytes");
}
控制台输出:
INFO com.baeldung.netbeanprofiler.SolApp -- byte[]
INFO com.baeldung.netbeanprofiler.SolApp -- Instances: 18996
INFO com.baeldung.netbeanprofiler.SolApp -- Total size: 2714375 bytes
INFO com.baeldung.netbeanprofiler.SolApp -- java.lang.String
INFO com.baeldung.netbeanprofiler.SolApp -- Instances: 18014
INFO com.baeldung.netbeanprofiler.SolApp -- Total size: 540420 bytes
INFO com.baeldung.netbeanprofiler.SolApp -- java.util.concurrent.ConcurrentHashMap$Node
INFO com.baeldung.netbeanprofiler.SolApp -- Instances: 5522
INFO com.baeldung.netbeanprofiler.SolApp -- Total size: 242968 bytes
关键发现:
byte[]
和String
类实例最多(18,996和18,014个)- 这符合预期,因为
SolarSystem
类底层依赖String
对象
6. 分析SolarSystem对象
6.1 实例数量与内存占用
获取SolarSystem
类的统计信息:
static void solarSystemSummary() {
JavaClass solarSystemClass = heap.getJavaClassByName("com.baeldung.netbeanprofiler.galaxy.SolarSystem");
List<Instance> instances = solarSystemClass.getInstances();
long totalSize = 0;
long instancesNumber = solarSystemClass.getInstancesCount();
for (Instance instance : instances) {
totalSize += instance.getSize();
}
LOGGER.info("Total SolarSystem instances: " + instancesNumber);
LOGGER.info("Total memory used by SolarSystem instances: " + totalSize);
}
输出结果:
INFO com.baeldung.netbeanprofiler.SolApp - Total SolarSystem instances: 1
INFO com.baeldung.netbeanprofiler.SolApp - Total memory used by SolarSystem instances: 36
验证结果:
- 仅创建1个
SolarSystem
实例 - 占用36字节内存
- 与示例代码中的实例化行为一致
6.2 字段详情分析
深入分析类的字段信息:
static void logFieldDetails() {
JavaClass solarSystemClass = heap.getJavaClassByName("com.baeldung.netbeanprofiler.galaxy.SolarSystem");
List<Field> fields = solarSystemClass.getFields();
for (Field field : fields) {
LOGGER.info("Field: " + field.getName());
LOGGER.info("Type: " + field.getType().getName());
}
}
此代码会输出SolarSystem
类所有字段的名称和类型。
7. 探索GC Roots
GC Roots能清晰反映垃圾回收器的行为。被GC Roots直接或间接引用的对象不会被回收,表明这些对象仍然存活。
获取所有GC Roots:
Collection<GCRoot> gcRoots = heap.getGCRoots();
分类统计不同类型的GC Roots:
int threadObj = 0, jniGlobal = 0, jniLocal = 0, javaFrame = 0, other = 0;
for (GCRoot gcRoot : gcRoots) {
Instance instance = gcRoot.getInstance();
String kind = gcRoot.getKind();
switch (kind) {
case THREAD_OBJECT:
threadObj++;
break;
case JNI_GLOBAL:
jniGlobal++;
break;
case JNI_LOCAL:
jniLocal++;
break;
case JAVA_FRAME:
javaFrame++;
break;
default:
other++;
}
}
关键类型说明:
- Thread Objects:堆转储时的活跃线程
- Java Frames:JVM栈帧中的对象引用
- JNI Global/Local:Java本地接口的全局/局部引用
输出统计结果:
LOGGER.info("\nGC Root Summary:");
LOGGER.info(" Thread Objects: " + threadObj);
LOGGER.info(" JNI Global References: " + jniGlobal);
LOGGER.info(" JNI Local References: " + jniLocal);
LOGGER.info(" Java Frames: " + javaFrame);
LOGGER.info(" Other: " + other);
控制台输出:
INFO com.baeldung.netbeanprofiler.SolApp -- Thread Objects: 8
INFO com.baeldung.netbeanprofiler.SolApp -- JNI Global References: 122
INFO com.baeldung.netbeanprofiler.SolApp -- JNI Local References: 1
INFO com.baeldung.netbeanprofiler.SolApp -- Java Frames: 481
INFO com.baeldung.netbeanprofiler.SolApp -- Other: 2000
关键数据:
- 存活线程:8个
- Java栈帧:481个
- JNI全局引用:122个
8. 总结
本文通过实际案例演示了NetBeans Profiler API的核心功能:
- ✅ 编程方式生成堆转储文件
- ✅ 获取堆转储概览信息
- ✅ 分析类实例分布
- ✅ 深入特定类分析
- ✅ 探索GC Roots结构
这些技术为自动化性能分析提供了坚实基础,特别适合集成到CI/CD流程中。完整示例代码可在GitHub仓库获取。