作为 Java 开发人员,我们熟悉我们的应用程序抛出 OutOfMemoryErrors 或我们的服务器监控工具抛出警报并抱怨 JVM 内存利用率高。
要调查内存问题,通常首先要查看 JVM 堆内存。
要进行此操作,我们可以先触发程序抛出 OutOfMemoryError,然后捕获堆转储。接下来我们将分析这个堆转储,以确定可能导致内存泄漏的潜在对象。
什么是堆转储(Heap Dumps)?
每当我们通过创建类的实例来创建 Java 对象时,它总是放置在称为堆的区域中。 Java 运行时的类也在这个堆中创建。
JVM 启动时会创建堆。它在运行时扩展或收缩以适应在我们的应用程序中创建或销毁的对象。
当堆满时,垃圾收集过程将运行以收集不再被引用的对象(即程序不再使用它们)。
堆转储包含Java应用程序当前正在使用的一些存活对象实例(注意:在堆内存中的存活对象)的快照。我们可以获得每个对象实例的详细信息,例如地址、类型、类名或大小,以及该实例是否有其他对象的引用。
堆转储有两种格式:
- 经典格式(the classic format)
- 便携式堆转储 (PHD) 格式(the Portable Heap Dump (PHD) format)
PHD 是默认格式。经典格式是人类可读的,因为它是 ASCII 文本,但 PHD 格式是二进制的,应通过适当的工具进行处理以进行分析。
生成 OutOfMemoryError 的示例程序
为了解释堆转储的分析,我们将使用一个简单的 Java 程序来生成 OutOfMemoryError:
public class OOMGenerator {
/**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
System.out.println("Max JVM memory: " + Runtime.getRuntime().maxMemory());
try {
ProductManager productManager = new ProductManager();
productManager.populateProducts();
} catch (OutOfMemoryError outofMemory) {
System.out.println("Catching out of memory error");
throw outofMemory;
}
}
}
public class ProductManager {
private static ProductGroup regularItems = new ProductGroup();
private static ProductGroup discountedItems = new ProductGroup();
public void populateProducts() {
int dummyArraySize =