06-Java语言核心-JVM原理-JVM内存区域详解

JVM内存区域详解

一、知识概述

Java虚拟机(JVM)在执行Java程序时,会将其管理的内存划分为若干个不同的数据区域。这些区域有各自的用途、创建时间和销毁时间。理解JVM内存区域是掌握Java性能优化、排查内存问题的基础。

JVM内存区域划分

根据《Java虚拟机规范》,JVM管理的内存分为以下几个区域:

复制代码
┌─────────────────────────────────────────────────────────────┐
│                        JVM 运行时数据区                       │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐         │
│  │   程序计数器  │  │   Java虚拟机栈 │  │   本地方法栈  │         │
│  │   (线程私有)  │  │   (线程私有)   │  │   (线程私有)  │         │
│  └─────────────┘  └─────────────┘  └─────────────┘         │
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │                    Java堆 (Heap)                      │   │
│  │                   (线程共享,GC主要区域)                │   │
│  │  ┌─────────────┐  ┌─────────────┐                    │   │
│  │  │   新生代     │  │    老年代    │                    │   │
│  │  │ Eden/S0/S1  │  │             │                    │   │
│  │  └─────────────┘  └─────────────┘                    │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │                   方法区 (Method Area)                │   │
│  │                   (线程共享,存储类信息)                │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │                   运行时常量池                         │   │
│  │               (方法区的一部分)                         │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │                   直接内存 (Direct Memory)            │   │
│  │               (NIO使用,非JVM规范定义)                 │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

线程私有 vs 线程共享

区域 类型 生命周期 存储内容
程序计数器 线程私有 线程生命周期 当前执行的字节码行号
Java虚拟机栈 线程私有 线程生命周期 栈帧(局部变量表、操作数栈等)
本地方法栈 线程私有 线程生命周期 Native方法调用信息
Java堆 线程共享 JVM生命周期 对象实例、数组
方法区 线程共享 JVM生命周期 类信息、常量、静态变量

二、知识点详细讲解

2.1 程序计数器(Program Counter Register)

程序计数器是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。

特点
  • 线程私有:每个线程都有独立的程序计数器
  • 唯一不会OOM的区域:如果执行的是Java方法,计数器记录的是字节码指令地址;如果执行的是Native方法,计数器值为空
  • 生命周期:随线程创建而创建,随线程结束而销毁
工作原理
java 复制代码
/**
 * 程序计数器工作原理演示
 */
public class ProgramCounterDemo {
    
    public static void main(String[] args) {
        int a = 1;          // PC: 0
        int b = 2;          // PC: 1
        int c = add(a, b);  // PC: 2 -> 跳转到add方法
        System.out.println(c); // PC: 3
    }
    
    public static int add(int x, int y) {
        return x + y;       // PC: add方法中的指令地址
    }
}

/*
字节码视角:
  public static void main(java.lang.String[]);
    Code:
       0: iconst_1          // PC = 0
       1: istore_1          // PC = 1
       2: iconst_2          // PC = 2
       3: istore_2          // PC = 3
       4: iload_1           // PC = 4
       5: iload_2           // PC = 5
       6: invokestatic #2   // PC = 6, 调用add方法
       9: istore_3          // PC = 9, 从方法返回后继续
      10: getstatic #3      // PC = 10
      ...
*/
多线程执行
复制代码
线程A                           线程B
┌─────────────┐                ┌─────────────┐
│ PC: 0x1000  │                │ PC: 0x2000  │
│ 执行方法A    │                │ 执行方法B    │
└─────────────┘                └─────────────┘
      ↓                              ↓
   时间片切换                     时间片切换
      ↓                              ↓
┌─────────────┐                ┌─────────────┐
│ PC: 0x1004  │                │ PC: 0x2008  │
│ 恢复执行     │                │ 恢复执行     │
└─────────────┘                └─────────────┘

每个线程有独立的PC,切换后能恢复到正确的执行位置

2.2 Java虚拟机栈(Java Virtual Machine Stack)

Java虚拟机栈描述的是Java方法执行的线程内存模型:每个方法被执行的时候,Java虚拟机都会同步创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态连接、方法出口等信息。

栈帧结构
复制代码
┌─────────────────────────────────────┐
│             栈帧 (Stack Frame)       │
├─────────────────────────────────────┤
│                                     │
│  ┌─────────────────────────────┐   │
│  │      局部变量表 (Local Variables)    │
│  │  [0] this                   │   │
│  │  [1] arg1                   │   │
│  │  [2] arg2                   │   │
│  │  [3] local_var1             │   │
│  │  [4] local_var2             │   │
│  └─────────────────────────────┘   │
│                                     │
│  ┌─────────────────────────────┐   │
│  │      操作数栈 (Operand Stack)       │
│  │        ┌───┐                     │   │
│  │        │ 5 │  <- 栈顶             │   │
│  │        ├───┤                     │   │
│  │        │ 3 │                      │   │
│  │        └───┘                     │   │
│  └─────────────────────────────┘   │
│                                     │
│  ┌─────────────────────────────┐   │
│  │      动态连接 (Dynamic Linking)     │
│  │  -> 指向运行时常量池的方法引用      │   │
│  └─────────────────────────────┘   │
│                                     │
│  ┌─────────────────────────────┐   │
│  │      方法返回地址 (Return Address)  │
│  │  -> 方法正常退出或异常退出的地址    │   │
│  └─────────────────────────────┘   │
│                                     │
│  ┌─────────────────────────────┐   │
│  │      附加信息 (附加信息)            │
│  └─────────────────────────────┘   │
│                                     │
└─────────────────────────────────────┘
局部变量表
java 复制代码
/**
 * 局部变量表示例
 */
public class LocalVariableTableDemo {
    
    // 局部变量表分析
    public static int calculate(int a, int b) {
        // 局部变量表:
        // Slot[0] = a (参数)
        // Slot[1] = b (参数)
        
        int c = a + b;     // Slot[2] = c (局部变量)
        int d = c * 2;     // Slot[3] = d (局部变量)
        
        {
            int e = d + 1; // Slot[4] = e (作用域内有效)
            // e 在此作用域结束后,Slot[4] 可被复用
        }
        
        int f = c - 1;     // Slot[4] = f (复用之前的Slot)
        
        return f;
    }
    
    // 实例方法的局部变量表
    public void instanceMethod(String name, int age) {
        // 局部变量表:
        // Slot[0] = this (实例方法隐含参数)
        // Slot[1] = name
        // Slot[2] = age
        
        System.out.println(name + ": " + age);
    }
    
    // 长整型和双精度浮点占用两个Slot
    public void doubleSlot(long id, double value) {
        // 局部变量表:
        // Slot[0] = this
        // Slot[1-2] = id (long占用两个Slot)
        // Slot[3-4] = value (double占用两个Slot)
    }
}
操作数栈
java 复制代码
/**
 * 操作数栈工作原理
 * 计算: (1 + 2) * 3
 */
public class OperandStackDemo {
    
    public static int calculate() {
        /*
        字节码执行过程:
        
        0: iconst_1      // 将常量1压入操作数栈
           操作数栈: [1]
        
        1: iconst_2      // 将常量2压入操作数栈
           操作数栈: [1, 2]
        
        2: iadd          // 弹出两个值相加,结果压栈
           操作数栈: [3]
        
        3: iconst_3      // 将常量3压入操作数栈
           操作数栈: [3, 3]
        
        4: imul          // 弹出两个值相乘,结果压栈
           操作数栈: [9]
        
        5: ireturn       // 返回栈顶值
        */
        return (1 + 2) * 3;
    }
    
    public static void main(String[] args) {
        int result = calculate();
        System.out.println(result);  // 输出: 9
    }
}
栈溢出错误
java 复制代码
/**
 * 栈溢出演示
 */
public class StackOverflowDemo {
    
    private static int count = 0;
    
    /**
     * 无限递归导致StackOverflowError
     */
    public static void recursiveCall() {
        count++;
        recursiveCall();  // 无限递归
    }
    
    /**
     * 通过减少局部变量延迟溢出
     */
    public static void smallFrame() {
        count++;
        smallFrame();  // 局部变量少,栈帧小
    }
    
    /**
     * 大量局部变量加速溢出
     */
    public static void largeFrame() {
        // 大量局部变量增加栈帧大小
        long a1, a2, a3, a4, a5, a6, a7, a8, a9, a10;
        long b1, b2, b3, b4, b5, b6, b7, b8, b9, b10;
        long c1, c2, c3, c4, c5, c6, c7, c8, c9, c10;
        long d1, d2, d3, d4, d5, d6, d7, d8, d9, d10;
        
        count++;
        largeFrame();
    }
    
    public static void main(String[] args) {
        try {
            recursiveCall();
        } catch (StackOverflowError e) {
            System.out.println("StackOverflowError at depth: " + count);
            System.out.println("栈深度取决于:");
            System.out.println("  1. -Xss 参数设置(默认1MB)");
            System.out.println("  2. 每个栈帧的大小");
            System.out.println("  3. 栈帧中局部变量的数量");
        }
        
        // 演示不同栈帧大小的影响
        count = 0;
        try {
            largeFrame();
        } catch (StackOverflowError e) {
            System.out.println("\nLargeFrame overflow at: " + count);
        }
    }
}

/*
运行示例:
  java -Xss256k StackOverflowDemo    # 小栈,快速溢出
  java -Xss1m StackOverflowDemo      # 默认大小
  java -Xss2m StackOverflowDemo      # 大栈,深度更大
*/

2.3 本地方法栈(Native Method Stack)

本地方法栈与Java虚拟机栈类似,区别在于Java虚拟机栈为Java方法服务,而本地方法栈为Native方法服务。

java 复制代码
/**
 * 本地方法栈示例
 */
public class NativeMethodStackDemo {
    
    public static void main(String[] args) {
        // Object.hashCode() 是native方法
        Object obj = new Object();
        int hash = obj.hashCode();  // 调用本地方法
        
        // System.currentTimeMillis() 是native方法
        long time = System.currentTimeMillis();  // 调用本地方法
        
        // Thread.start() 最终调用native方法启动线程
        Thread thread = new Thread(() -> {
            System.out.println("Thread running");
        });
        thread.start();  // 内部调用native start0()
        
        // Class.forName() 内部使用native方法
        try {
            Class<?> clazz = Class.forName("java.lang.String");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        
        // Unsafe类大量使用native方法
        // sun.misc.Unsafe 提供直接内存访问能力
    }
}

/*
本地方法示例:

// Object类
public native int hashCode();
protected native Object clone() throws CloneNotSupportedException;

// System类
public static native long currentTimeMillis();
public static native void arraycopy(Object src, int srcPos,
                                    Object dest, int destPos, int length);

// Thread类
private native void start0();
private native boolean isInterrupted(boolean clearInterrupted);

// Class类
private native String getName0();
*/

2.4 Java堆(Java Heap)

Java堆是Java虚拟机管理的内存中最大的一块,被所有线程共享,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。

堆内存结构
复制代码
┌───────────────────────────────────────────────────────────────┐
│                        Java Heap                               │
├───────────────────────────────────────────────────────────────┤
│                                                                │
│  ┌─────────────────────────────────┐  ┌─────────────────────┐ │
│  │          新生代 (Young Gen)      │  │   老年代 (Old Gen)   │ │
│  │         (约占堆的1/3)            │  │   (约占堆的2/3)      │ │
│  │  ┌───────────┬──────┬──────┐    │  │                     │ │
│  │  │   Eden    │ S0   │ S1   │    │  │                     │ │
│  │  │  (8/10)   │(1/10)│(1/10)│    │  │                     │ │
│  │  └───────────┴──────┴──────┘    │  │                     │ │
│  │                                 │  │                     │ │
│  │  对象先在Eden分配               │  │   长期存活的对象     │ │
│  │  GC后存活对象进入Survivor       │  │   从Survivor晋升    │ │
│  │  达到年龄阈值后晋升老年代       │  │                     │ │
│  └─────────────────────────────────┘  └─────────────────────┘ │
│                                                                │
│  新生代GC (Minor GC)                老年代GC (Major/Full GC)   │
│  频繁、速度快                       较少、速度慢                │
│                                                                │
└───────────────────────────────────────────────────────────────┘

相关参数:
  -Xms: 堆初始大小
  -Xmx: 堆最大大小
  -Xmn: 新生代大小
  -XX:NewRatio: 新生代与老年代比例
  -XX:SurvivorRatio: Eden与Survivor比例
  -XX:MaxTenuringThreshold: 晋升老年代年龄阈值
对象分配过程
java 复制代码
/**
 * 对象在堆中的分配过程
 */
public class HeapAllocationDemo {
    
    public static void main(String[] args) {
        /*
        对象分配流程:
        
        1. 新对象先尝试在Eden区分配
           ┌─────────────────────────────────┐
           │  Eden: [obj1][obj2][obj3][... ] │
           └─────────────────────────────────┘
        
        2. Eden区满了,触发Minor GC
           存活对象被复制到Survivor区(假设S0)
           ┌─────────────────────────────────┐
           │  Eden: [空]                     │
           └─────────────────────────────────┘
           ┌──────┐
           │ S0:  │ [存活obj1][存活obj2]
           └──────┘
        
        3. 下次Minor GC,Eden和S0中存活对象复制到S1
           ┌─────────────────────────────────┐
           │  Eden: [空]                     │
           └─────────────────────────────────┘
           ┌──────┐        ┌──────┐
           │ S0:  │ [空]   │ S1:  │ [存活对象]
           └──────┘        └──────┘
        
        4. Survivor中对象年龄达到阈值,晋升老年代
           ┌──────┐
           │ Old: │ [年龄足够的老对象]
           └──────┘
        
        5. 大对象直接进入老年代
           -XX:PretenureSizeThreshold 设置阈值
        */
        
        // 普通对象在Eden分配
        for (int i = 0; i < 100; i++) {
            byte[] data = new byte[1024];  // 小对象,Eden分配
        }
        
        // 大对象可能直接进入老年代
        byte[] largeData = new byte[10 * 1024 * 1024];  // 10MB
        
        // 长期存活对象最终进入老年代
        List<byte[]> longLived = new ArrayList<>();
        for (int i = 0; i < 1000; i++) {
            longLived.add(new byte[1024]);
        }
    }
}
堆内存溢出
java 复制代码
import java.util.*;

/**
 * 堆内存溢出演示
 * java -Xms20m -Xmx20m HeapOOMDemo
 */
public class HeapOOMDemo {
    
    static class OOMObject {
        private byte[] data = new byte[1024 * 1024];  // 1MB
    }
    
    public static void main(String[] args) {
        List<OOMObject> list = new ArrayList<>();
        
        System.out.println("开始创建对象...");
        System.out.println("堆大小: 20MB");
        System.out.println("每个对象: 1MB + 对象头");
        System.out.println();
        
        try {
            int count = 0;
            while (true) {
                list.add(new OOMObject());
                count++;
                if (count % 5 == 0) {
                    System.out.println("已创建 " + count + " 个对象");
                }
            }
        } catch (OutOfMemoryError e) {
            System.out.println("\nOutOfMemoryError: Java heap space");
            System.out.println("总共创建了约 " + list.size() + " 个对象");
            System.out.println("\n解决方法:");
            System.out.println("  1. 增大堆内存: -Xmx");
            System.out.println("  2. 检查是否存在内存泄漏");
            System.out.println("  3. 使用内存分析工具: jvisualvm, MAT");
        }
    }
}

/*
运行结果:
  开始创建对象...
  堆大小: 20MB
  每个对象: 1MB + 对象头
  
  已创建 5 个对象
  已创建 10 个对象
  已创建 15 个对象
  
  OutOfMemoryError: Java heap space
  总共创建了约 18 个对象
*/
堆内存分析
java 复制代码
import java.lang.management.*;
import java.util.*;

/**
 * 堆内存使用情况监控
 */
public class HeapMonitorDemo {
    
    public static void main(String[] args) throws InterruptedException {
        // 获取内存管理Bean
        MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();
        MemoryUsage heapUsage = memoryMXBean.getHeapMemoryUsage();
        
        System.out.println("=== 堆内存配置 ===");
        System.out.println("初始大小: " + toMB(heapUsage.getInit()) + " MB");
        System.out.println("当前使用: " + toMB(heapUsage.getUsed()) + " MB");
        System.out.println("已提交: " + toMB(heapUsage.getCommitted()) + " MB");
        System.out.println("最大可用: " + toMB(heapUsage.getMax()) + " MB");
        
        // 获取各个内存池信息
        System.out.println("\n=== 内存池详情 ===");
        List<MemoryPoolMXBean> memoryPools = ManagementFactory.getMemoryPoolMXBeans();
        
        for (MemoryPoolMXBean pool : memoryPools) {
            if (pool.getType() == MemoryType.HEAP) {
                MemoryUsage usage = pool.getUsage();
                System.out.println("\n" + pool.getName() + ":");
                System.out.println("  使用: " + toMB(usage.getUsed()) + " MB");
                System.out.println("  已提交: " + toMB(usage.getCommitted()) + " MB");
                System.out.println("  最大: " + toMB(usage.getMax()) + " MB");
            }
        }
        
        // 模拟内存使用
        System.out.println("\n=== 创建对象后 ===");
        List<byte[]> data = new ArrayList<>();
        for (int i = 0; i < 100; i++) {
            data.add(new byte[1024 * 1024]);  // 1MB
        }
        
        heapUsage = memoryMXBean.getHeapMemoryUsage();
        System.out.println("当前使用: " + toMB(heapUsage.getUsed()) + " MB");
        
        // GC前后的内存变化
        System.out.println("\n=== 执行GC ===");
        long beforeGC = memoryMXBean.getHeapMemoryUsage().getUsed();
        System.out.println("GC前: " + toMB(beforeGC) + " MB");
        
        System.gc();
        Thread.sleep(100);
        
        long afterGC = memoryMXBean.getHeapMemoryUsage().getUsed();
        System.out.println("GC后: " + toMB(afterGC) + " MB");
        System.out.println("回收: " + toMB(beforeGC - afterGC) + " MB");
    }
    
    private static String toMB(long bytes) {
        return String.format("%.2f", bytes / 1024.0 / 1024.0);
    }
}

2.5 方法区(Method Area)

方法区与Java堆一样,是各个线程共享的内存区域,用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。

方法区存储内容
复制代码
┌─────────────────────────────────────────────────────────────┐
│                       方法区 (Method Area)                   │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │                    类型信息                           │   │
│  │  - 类名、访问修饰符                                   │   │
│  │  - 父类、实现的接口                                   │   │
│  │  - 字段信息(名称、类型、修饰符)                     │   │
│  │  - 方法信息(名称、返回类型、参数、字节码)           │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │                    运行时常量池                       │   │
│  │  - 字面量(字符串、final常量)                       │   │
│  │  - 符号引用(类和接口的全限定名、字段和方法描述)     │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │                    静态变量                           │   │
│  │  - static 修饰的类变量                               │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │                    JIT编译代码                        │   │
│  │  - 即时编译器生成的本地代码                          │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

JDK版本差异:
  JDK 7及之前: 永久代 (Permanent Generation)
    - -XX:PermSize, -XX:MaxPermSize
    - 固定大小,容易OOM
  
  JDK 8及之后: 元空间 (Metaspace)
    - -XX:MetaspaceSize, -XX:MaxMetaspaceSize
    - 使用本地内存,默认无上限
运行时常量池
java 复制代码
/**
 * 运行时常量池示例
 */
public class RuntimeConstantPoolDemo {
    
    public static void main(String[] args) {
        // === 字符串常量池 ===
        
        // 字面量创建,放入字符串常量池
        String s1 = "Hello";
        String s2 = "Hello";
        System.out.println("s1 == s2: " + (s1 == s2));  // true
        
        // new创建,堆中创建新对象
        String s3 = new String("Hello");
        System.out.println("s1 == s3: " + (s1 == s3));  // false
        System.out.println("s1.intern() == s3.intern(): " + 
            (s1.intern() == s3.intern()));  // true
        
        // === 编译期常量 ===
        System.out.println("\n编译期常量:");
        System.out.println("MAX_VALUE = " + Integer.MAX_VALUE);
        System.out.println("PI = " + Math.PI);
        
        // === 符号引用 -> 直接引用 ===
        System.out.println("\n符号引用解析:");
        
        // 解析前:java.lang.Object 是符号引用
        // 解析后:指向方法区中Object类的直接指针
        Object obj = new Object();
        
        // 方法调用的符号引用
        // 编译时:只知道方法名和描述符
        // 运行时:解析为方法的直接引用(入口地址)
        String str = obj.toString();
        System.out.println(str);
        
        // === 常量池溢出 (JDK 7之前) ===
        // JDK 7之后,字符串常量池移到堆中
        System.out.println("\n常量池容量测试:");
        testStringPool();
    }
    
    /**
     * 测试字符串常量池
     * JDK 7+: 池在堆中,受堆大小限制
     * JDK 6: 池在永久代,受PermGen限制
     */
    private static void testStringPool() {
        Set<String> pool = new HashSet<>();
        long start = System.currentTimeMillis();
        
        try {
            int i = 0;
            while (true) {
                // intern() 将字符串加入常量池
                String str = "String" + i;
                str.intern();
                pool.add(str);
                i++;
                
                if (i % 100000 == 0) {
                    System.out.println("已添加 " + i + " 个字符串到常量池");
                }
            }
        } catch (OutOfMemoryError e) {
            long end = System.currentTimeMillis();
            System.out.println("OOM after adding " + pool.size() + " strings");
            System.out.println("Time: " + (end - start) + "ms");
        }
    }
    
    /**
     * Class常量池内容
     */
    public static void showClassConstantPool() throws Exception {
        // 使用javap查看常量池
        // javap -v RuntimeConstantPoolDemo.class
        
        /*
        Constant pool:
           #1 = Methodref          #10.#29        // java/lang/Object."<init>":()V
           #2 = String             #30            // Hello
           #3 = Fieldref           #31.#32        // java/lang/System.out:Ljava/io/PrintStream;
           #4 = Class              #33            // java/lang/StringBuilder
           #5 = Methodref          #34.#35        // ...StringBuilder.append:(Ljava/lang/String;)...
           #6 = Methodref          #34.#36        // ...StringBuilder.toString:()Ljava/lang/String;
           ...
        */
    }
}
方法区溢出
java 复制代码
import java.lang.reflect.*;
import java.util.*;

/**
 * 方法区溢出演示
 * JDK 8: java -XX:MaxMetaspaceSize=10m MetaspaceOOMDemo
 * JDK 7: java -XX:MaxPermSize=10m PermGenOOMDemo
 */
public class MetaspaceOOMDemo {
    
    public static void main(String[] args) {
        System.out.println("=== 方法区/元空间溢出演示 ===");
        System.out.println("限制: 10MB");
        System.out.println();
        
        // 使用CGLib动态生成大量类
        Enhancer enhancer = new Enhancer();
        
        try {
            int count = 0;
            while (true) {
                // 每次创建一个新的类
                enhancer.setSuperclass(OOMObject.class);
                enhancer.setUseCache(false);
                enhancer.setCallback(new MethodInterceptor() {
                    @Override
                    public Object intercept(Object obj, Method method, 
                            Object[] args, MethodProxy proxy) throws Throwable {
                        return proxy.invokeSuper(obj, args);
                    }
                });
                
                Object obj = enhancer.create();
                count++;
                
                if (count % 100 == 0) {
                    System.out.println("已生成 " + count + " 个类");
                }
            }
        } catch (OutOfMemoryError e) {
            System.out.println("\nOutOfMemoryError: Metaspace");
            System.out.println("\n原因:");
            System.out.println("  1. 加载太多类(CGLib、动态代理)");
            System.out.println("  2. 元空间/永久代设置过小");
            System.out.println("  3. 类加载器泄漏");
            System.out.println("\n解决方法:");
            System.out.println("  JDK 8+: 增大 -XX:MaxMetaspaceSize");
            System.out.println("  JDK 7: 增大 -XX:MaxPermSize");
            System.out.println("  使用类加载器分析工具");
        }
    }
    
    static class OOMObject {
        public void method() {}
    }
}

// 需要CGLib依赖
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

2.6 直接内存(Direct Memory)

直接内存不是虚拟机运行时数据区的一部分,也不是《Java虚拟机规范》中定义的内存区域。但是这部分内存也被频繁地使用,而且也可能导致OutOfMemoryError异常。

java 复制代码
import java.nio.*;
import java.nio.channels.*;
import java.io.*;
import java.util.*;

/**
 * 直接内存示例
 */
public class DirectMemoryDemo {
    
    public static void main(String[] args) throws Exception {
        
        // === 1. 直接内存 vs 堆内存 ===
        System.out.println("=== 直接内存 vs 堆内存 ===\n");
        
        // 堆内存Buffer
        ByteBuffer heapBuffer = ByteBuffer.allocate(1024 * 1024);  // 1MB堆内
        System.out.println("Heap Buffer:");
        System.out.println("  isDirect: " + heapBuffer.isDirect());
        System.out.println("  在Java堆中分配");
        System.out.println("  IO时需要复制到本地内存");
        
        // 直接内存Buffer
        ByteBuffer directBuffer = ByteBuffer.allocateDirect(1024 * 1024);  // 1MB堆外
        System.out.println("\nDirect Buffer:");
        System.out.println("  isDirect: " + directBuffer.isDirect());
        System.out.println("  在本地内存中分配");
        System.out.println("  IO时直接使用,零拷贝");
        
        // === 2. 性能对比 ===
        System.out.println("\n=== IO性能对比 ===\n");
        
        // 准备测试文件
        File tempFile = File.createTempFile("test", ".dat");
        tempFile.deleteOnExit();
        writeTestData(tempFile, 100 * 1024 * 1024);  // 100MB
        
        // 堆内存读写
        long heapTime = testHeapIO(tempFile);
        System.out.println("Heap Buffer IO: " + heapTime + "ms");
        
        // 直接内存读写
        long directTime = testDirectIO(tempFile);
        System.out.println("Direct Buffer IO: " + directTime + "ms");
        
        System.out.println("\n直接内存快 " + 
            String.format("%.1f", (double)heapTime / directTime) + " 倍");
        
        // === 3. 直接内存监控 ===
        System.out.println("\n=== 直接内存使用 ===\n");
        
        // 查看直接内存限制
        long maxDirect = sun.misc.VM.maxDirectMemory();
        System.out.println("最大直接内存: " + toMB(maxDirect) + " MB");
        
        // 分配直接内存
        List<ByteBuffer> buffers = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            buffers.add(ByteBuffer.allocateDirect(10 * 1024 * 1024));  // 10MB each
        }
        
        // 查看已使用
        long usedDirect = getUsedDirectMemory();
        System.out.println("已使用直接内存: " + toMB(usedDirect) + " MB");
        
        // === 4. 直接内存溢出 ===
        System.out.println("\n=== 直接内存溢出测试 ===");
        System.out.println("限制: -XX:MaxDirectMemorySize=50m");
        System.out.println();
        
        try {
            // 分配超过限制的直接内存
            ByteBuffer.allocateDirect(100 * 1024 * 1024);  // 100MB
        } catch (OutOfMemoryError e) {
            System.out.println("OutOfMemoryError: Direct buffer memory");
            System.out.println("\n原因:");
            System.out.println("  直接内存超过 -XX:MaxDirectMemorySize 限制");
            System.out.println("\n解决方法:");
            System.out.println("  1. 增大 -XX:MaxDirectMemorySize");
            System.out.println("  2. 及时释放DirectBuffer");
            System.out.println("  3. 使用 Cleaner 清理");
        }
        
        // 清理临时文件
        tempFile.delete();
    }
    
    private static void writeTestData(File file, long size) throws IOException {
        try (RandomAccessFile raf = new RandomAccessFile(file, "rw")) {
            raf.setLength(size);
        }
    }
    
    private static long testHeapIO(File file) throws IOException {
        long start = System.currentTimeMillis();
        
        try (FileChannel channel = new FileInputStream(file).getChannel()) {
            ByteBuffer buffer = ByteBuffer.allocate(1024 * 1024);
            while (channel.read(buffer) != -1) {
                buffer.clear();
            }
        }
        
        return System.currentTimeMillis() - start;
    }
    
    private static long testDirectIO(File file) throws IOException {
        long start = System.currentTimeMillis();
        
        try (FileChannel channel = new FileInputStream(file).getChannel()) {
            ByteBuffer buffer = ByteBuffer.allocateDirect(1024 * 1024);
            while (channel.read(buffer) != -1) {
                buffer.clear();
            }
        }
        
        return System.currentTimeMillis() - start;
    }
    
    private static long getUsedDirectMemory() {
        try {
            Class<?> bitsClass = Class.forName("java.nio.Bits");
            Method method = bitsClass.getDeclaredMethod("reservedMemory");
            method.setAccessible(true);
            return (Long) method.invoke(null);
        } catch (Exception e) {
            return -1;
        }
    }
    
    private static String toMB(long bytes) {
        return String.format("%.2f", bytes / 1024.0 / 1024.0);
    }
}

/*
运行示例:
  java -XX:MaxDirectMemorySize=100m DirectMemoryDemo

输出:
  === 直接内存 vs 堆内存 ===
  
  Heap Buffer:
    isDirect: false
    在Java堆中分配
    IO时需要复制到本地内存
  
  Direct Buffer:
    isDirect: true
    在本地内存中分配
    IO时直接使用,零拷贝
  
  === IO性能对比 ===
  
  Heap Buffer IO: 1523ms
  Direct Buffer IO: 421ms
  
  直接内存快 3.6 倍
  
  最大直接内存: 100.00 MB
  已使用直接内存: 100.16 MB
*/

三、可运行Java代码示例

完整示例:JVM内存监控工具

java 复制代码
import java.lang.management.*;
import java.util.*;
import java.util.concurrent.*;

/**
 * JVM内存监控工具
 * 综合监控各个内存区域的使用情况
 */
public class JVMMemoryMonitor {
    
    private final MemoryMXBean memoryMXBean;
    private final List<MemoryPoolMXBean> memoryPools;
    private volatile boolean running = true;
    
    public JVMMemoryMonitor() {
        this.memoryMXBean = ManagementFactory.getMemoryMXBean();
        this.memoryPools = ManagementFactory.getMemoryPoolMXBeans();
    }
    
    /**
     * 打印内存概览
     */
    public void printMemoryOverview() {
        System.out.println("╔════════════════════════════════════════════════════════════╗");
        System.out.println("║                    JVM 内存概览                             ║");
        System.out.println("╚════════════════════════════════════════════════════════════╝");
        
        // 堆内存
        MemoryUsage heapUsage = memoryMXBean.getHeapMemoryUsage();
        System.out.println("\n【堆内存 Heap】");
        printMemoryUsage(heapUsage);
        
        // 非堆内存(方法区等)
        MemoryUsage nonHeapUsage = memoryMXBean.getNonHeapMemoryUsage();
        System.out.println("\n【非堆内存 Non-Heap】");
        printMemoryUsage(nonHeapUsage);
        
        // 各内存池详情
        System.out.println("\n【内存池详情】");
        for (MemoryPoolMXBean pool : memoryPools) {
            System.out.println("\n  " + pool.getName());
            System.out.println("  ├─ 类型: " + pool.getType());
            System.out.println("  ├─ 内存管理器: " + 
                String.join(", ", pool.getMemoryManagerNames()));
            printMemoryUsage(pool.getUsage(), "  └─ ");
        }
    }
    
    /**
     * 打印内存使用情况
     */
    private void printMemoryUsage(MemoryUsage usage) {
        printMemoryUsage(usage, "");
    }
    
    private void printMemoryUsage(MemoryUsage usage, String prefix) {
        long used = usage.getUsed();
        long committed = usage.getCommitted();
        long max = usage.getMax();
        
        System.out.println(prefix + "已使用: " + formatBytes(used));
        System.out.println(prefix + "已提交: " + formatBytes(committed));
        System.out.println(prefix + "最大值: " + 
            (max == -1 ? "未定义" : formatBytes(max)));
        
        if (max != -1) {
            double usagePercent = (double) used / max * 100;
            System.out.println(prefix + "使用率: " + 
                String.format("%.2f%%", usagePercent));
            printUsageBar(usagePercent, prefix);
        }
    }
    
    /**
     * 打印使用率条形图
     */
    private void printUsageBar(double percent, String prefix) {
        int barLength = 40;
        int filled = (int) (percent / 100 * barLength);
        
        StringBuilder bar = new StringBuilder(prefix + "[");
        for (int i = 0; i < barLength; i++) {
            if (i < filled) {
                bar.append("█");
            } else {
                bar.append("░");
            }
        }
        bar.append("] ").append(String.format("%.1f%%", percent));
        System.out.println(bar);
    }
    
    /**
     * 格式化字节大小
     */
    private String formatBytes(long bytes) {
        if (bytes < 1024) return bytes + " B";
        if (bytes < 1024 * 1024) return String.format("%.2f KB", bytes / 1024.0);
        if (bytes < 1024 * 1024 * 1024) 
            return String.format("%.2f MB", bytes / 1024.0 / 1024.0);
        return String.format("%.2f GB", bytes / 1024.0 / 1024.0 / 1024.0);
    }
    
    /**
     * 实时监控内存
     */
    public void startMonitoring(long intervalMs) {
        ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
        
        scheduler.scheduleAtFixedRate(() -> {
            if (!running) return;
            
            MemoryUsage heapUsage = memoryMXBean.getHeapMemoryUsage();
            double heapUsagePercent = (double) heapUsage.getUsed() / 
                heapUsage.getMax() * 100;
            
            System.out.printf("[%s] 堆内存: %.1f%% | 非堆: %s%n",
                new java.text.SimpleDateFormat("HH:mm:ss").format(new Date()),
                heapUsagePercent,
                formatBytes(memoryMXBean.getNonHeapMemoryUsage().getUsed()));
            
            // 警告
            if (heapUsagePercent > 80) {
                System.out.println("  ⚠️  警告: 堆内存使用率超过80%!");
            }
            
        }, 0, intervalMs, TimeUnit.MILLISECONDS);
        
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            running = false;
            scheduler.shutdown();
        }));
    }
    
    /**
     * 模拟内存使用
     */
    public void simulateMemoryUsage() {
        System.out.println("\n╔════════════════════════════════════════════════════════════╗");
        System.out.println("║                    内存使用模拟                             ║");
        System.out.println("╚════════════════════════════════════════════════════════════╝\n");
        
        List<byte[]> memoryHog = new ArrayList<>();
        Random random = new Random();
        
        try {
            for (int i = 0; i < 100; i++) {
                // 随机分配不同大小的对象
                int size = random.nextInt(1024 * 1024);  // 0-1MB
                memoryHog.add(new byte[size]);
                
                if (i % 10 == 0) {
                    System.out.println("分配了 " + (i + 1) + " 个对象");
                    MemoryUsage usage = memoryMXBean.getHeapMemoryUsage();
                    System.out.println("  堆内存使用: " + formatBytes(usage.getUsed()));
                }
                
                Thread.sleep(100);
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } catch (OutOfMemoryError e) {
            System.out.println("\n❌ OutOfMemoryError: " + e.getMessage());
        }
        
        // 触发GC
        System.out.println("\n触发GC...");
        memoryHog.clear();
        System.gc();
        
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        
        MemoryUsage usage = memoryMXBean.getHeapMemoryUsage();
        System.out.println("GC后堆内存: " + formatBytes(usage.getUsed()));
    }
    
    /**
     * 线程栈信息
     */
    public void printThreadStackInfo() {
        System.out.println("\n╔════════════════════════════════════════════════════════════╗");
        System.out.println("║                    线程栈信息                               ║");
        System.out.println("╚════════════════════════════════════════════════════════════╝\n");
        
        ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
        
        System.out.println("活动线程数: " + threadMXBean.getThreadCount());
        System.out.println("峰值线程数: " + threadMXBean.getPeakThreadCount());
        System.out.println("总启动线程数: " + threadMXBean.getTotalStartedThreadCount());
        System.out.println("守护线程数: " + threadMXBean.getDaemonThreadCount());
        
        // 线程详情
        System.out.println("\n线程列表:");
        for (ThreadInfo info : threadMXBean.dumpAllThreads(false, false)) {
            System.out.println("  - " + info.getThreadName() + 
                " [ID: " + info.getThreadId() + 
                ", State: " + info.getThreadState() + "]");
        }
    }
    
    public static void main(String[] args) throws InterruptedException {
        JVMMemoryMonitor monitor = new JVMMemoryMonitor();
        
        // 打印内存概览
        monitor.printMemoryOverview();
        
        // 打印线程信息
        monitor.printThreadStackInfo();
        
        // 启动实时监控(每2秒)
        System.out.println("\n开始实时监控(每2秒)...\n");
        monitor.startMonitoring(2000);
        
        // 模拟内存使用
        monitor.simulateMemoryUsage();
        
        // 持续运行一段时间
        Thread.sleep(10000);
        
        System.out.println("\n监控结束");
        System.exit(0);
    }
}

/*
运行示例:
  java -Xms100m -Xmx200m -XX:MaxMetaspaceSize=50m JVMMemoryMonitor
*/

四、实战应用场景

场景1:内存泄漏分析

java 复制代码
import java.util.*;
import java.lang.ref.*;

/**
 * 内存泄漏场景与诊断
 */
public class MemoryLeakDemo {
    
    // 场景1:静态集合导致的内存泄漏
    static class StaticCollectionLeak {
        private static final Map<String, Object> CACHE = new HashMap<>();
        
        public void addToCache(String key, Object value) {
            CACHE.put(key, value);
            // 问题:只添加不删除,导致内存泄漏
        }
    }
    
    // 场景2:未关闭的资源
    static class ResourceLeak {
        public void readData() throws IOException {
            FileInputStream fis = new FileInputStream("data.txt");
            // 问题:未关闭流,导致native内存泄漏
            // 应该使用 try-with-resources
        }
        
        public void readDataCorrect() throws IOException {
            try (FileInputStream fis = new FileInputStream("data.txt")) {
                // 正确:自动关闭资源
            }
        }
    }
    
    // 场景3:监听器未注销
    static class ListenerLeak {
        private List<EventListener> listeners = new ArrayList<>();
        
        public void addListener(EventListener listener) {
            listeners.add(listener);
            // 问题:从未移除监听器
        }
        
        public void removeListener(EventListener listener) {
            listeners.remove(listener);
            // 解决:提供移除方法
        }
    }
    
    // 场景4:ThreadLocal未清理
    static class ThreadLocalLeak {
        private static final ThreadLocal<byte[]> threadLocal = 
            new ThreadLocal<>();
        
        public void processData() {
            threadLocal.set(new byte[1024 * 1024]);  // 1MB
            // 问题:线程池场景下,线程复用导致数据残留
            // 解决:finally中清理
        }
        
        public void processDataCorrect() {
            try {
                threadLocal.set(new byte[1024 * 1024]);
                // 处理数据
            } finally {
                threadLocal.remove();  // 清理
            }
        }
    }
    
    // 使用弱引用避免泄漏
    static class WeakReferenceCache {
        private final Map<String, WeakReference<Object>> cache = 
            new HashMap<>();
        
        public void put(String key, Object value) {
            cache.put(key, new WeakReference<>(value));
        }
        
        public Object get(String key) {
            WeakReference<Object> ref = cache.get(key);
            return ref != null ? ref.get() : null;
        }
    }
    
    // 内存泄漏诊断
    public static void main(String[] args) throws InterruptedException {
        System.out.println("=== 内存泄漏诊断示例 ===\n");
        
        StaticCollectionLeak leak = new StaticCollectionLeak();
        
        // 记录初始内存
        long initialMemory = Runtime.getRuntime().totalMemory() - 
            Runtime.getRuntime().freeMemory();
        
        // 模拟内存泄漏
        for (int i = 0; i < 10000; i++) {
            leak.addToCache("key" + i, new byte[1024]);
        }
        
        // 触发GC
        System.gc();
        Thread.sleep(1000);
        
        // 检查内存变化
        long afterGC = Runtime.getRuntime().totalMemory() - 
            Runtime.getRuntime().freeMemory();
        
        System.out.println("初始内存: " + (initialMemory / 1024 / 1024) + " MB");
        System.out.println("GC后内存: " + (afterGC / 1024 / 1024) + " MB");
        System.out.println("内存增长: " + ((afterGC - initialMemory) / 1024 / 1024) + " MB");
        
        if (afterGC > initialMemory * 2) {
            System.out.println("\n⚠️ 可能存在内存泄漏!");
            System.out.println("诊断步骤:");
            System.out.println("  1. 使用 jmap -histo <pid> 查看对象分布");
            System.out.println("  2. 使用 jvisualvm 或 MAT 分析堆转储");
            System.out.println("  3. 查找大对象和GC Root路径");
        }
    }
}

场景2:OOM应急处理

java 复制代码
import java.util.*;
import java.lang.management.*;

/**
 * OOM应急处理工具
 */
public class OOMHandler {
    
    /**
     * 注册OOM处理器
     * 注意:仅用于应急,不能保证一定能执行
     */
    public static void registerOOMHandler() {
        // 方法1:捕获OutOfMemoryError
        Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> {
            if (throwable instanceof OutOfMemoryError) {
                handleOOM((OutOfMemoryError) throwable);
            }
        });
        
        // 方法2:使用MXBean监控
        MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();
        
        // 方法3:预分配应急内存
        final byte[] emergencyBuffer = new byte[10 * 1024 * 1024];  // 10MB
        
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            System.out.println("\n=== OOM 应急处理 ===");
            System.out.println("正在生成诊断信息...");
            
            // 释放应急内存
            // emergencyBuffer = null;
            
            // 生成诊断信息
            generateDiagnosticReport();
        }));
    }
    
    /**
     * 处理OOM
     */
    private static void handleOOM(OutOfMemoryError error) {
        System.out.println("\n╔════════════════════════════════════════════════════════════╗");
        System.out.println("║                    OutOfMemoryError                        ║");
        System.out.println("╚════════════════════════════════════════════════════════════╝");
        
        String message = error.getMessage();
        
        if (message.contains("Java heap space")) {
            System.out.println("\n类型: 堆内存溢出");
            System.out.println("原因:");
            System.out.println("  1. 对象过多,内存不足");
            System.out.println("  2. 存在内存泄漏");
            System.out.println("  3. 堆大小设置不合理");
            System.out.println("\n解决方法:");
            System.out.println("  - 增大堆内存: -Xmx");
            System.out.println("  - 分析内存泄漏");
            System.out.println("  - 优化对象使用");
            
        } else if (message.contains("Metaspace")) {
            System.out.println("\n类型: 元空间溢出");
            System.out.println("原因:");
            System.out.println("  1. 加载类过多");
            System.out.println("  2. 动态代理、CGLib生成类过多");
            System.out.println("  3. 元空间设置过小");
            System.out.println("\n解决方法:");
            System.out.println("  - 增大元空间: -XX:MaxMetaspaceSize");
            System.out.println("  - 检查类加载器泄漏");
            System.out.println("  - 限制动态类生成");
            
        } else if (message.contains("GC overhead limit exceeded")) {
            System.out.println("\n类型: GC时间过长");
            System.out.println("原因:");
            System.out.println("  应用花费了过多时间在GC上但回收很少");
            System.out.println("\n解决方法:");
            System.out.println("  - 增大堆内存");
            System.out.println("  - 优化对象生命周期");
            System.out.println("  - 禁用此限制: -XX:-UseGCOverheadLimit");
            
        } else if (message.contains("Direct buffer memory")) {
            System.out.println("\n类型: 直接内存溢出");
            System.out.println("原因:");
            System.out.println("  NIO直接内存使用过多");
            System.out.println("\n解决方法:");
            System.out.println("  - 增大限制: -XX:MaxDirectMemorySize");
            System.out.println("  - 及时释放DirectBuffer");
        }
        
        // 生成诊断报告
        generateDiagnosticReport();
    }
    
    /**
     * 生成诊断报告
     */
    private static void generateDiagnosticReport() {
        System.out.println("\n╔════════════════════════════════════════════════════════════╗");
        System.out.println("║                    诊断报告                                 ║");
        System.out.println("╚════════════════════════════════════════════════════════════╝\n");
        
        // JVM信息
        Runtime runtime = Runtime.getRuntime();
        System.out.println("【JVM内存】");
        System.out.println("  最大内存: " + toMB(runtime.maxMemory()) + " MB");
        System.out.println("  总内存: " + toMB(runtime.totalMemory()) + " MB");
        System.out.println("  空闲内存: " + toMB(runtime.freeMemory()) + " MB");
        System.out.println("  已用内存: " + 
            toMB(runtime.totalMemory() - runtime.freeMemory()) + " MB");
        
        // 内存池
        System.out.println("\n【内存池】");
        for (MemoryPoolMXBean pool : ManagementFactory.getMemoryPoolMXBeans()) {
            MemoryUsage usage = pool.getUsage();
            System.out.println("  " + pool.getName() + ": " + 
                toMB(usage.getUsed()) + " / " + 
                (usage.getMax() == -1 ? "无限制" : toMB(usage.getMax())) + " MB");
        }
        
        // 线程信息
        ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
        System.out.println("\n【线程】");
        System.out.println("  活动线程: " + threadMXBean.getThreadCount());
        System.out.println("  峰值线程: " + threadMXBean.getPeakThreadCount());
        
        // 建议
        System.out.println("\n【排查建议】");
        System.out.println("  1. 生成堆转储: jmap -dump:format=b,file=heap.hprof <pid>");
        System.out.println("  2. 查看对象分布: jmap -histo <pid>");
        System.out.println("  3. 使用MAT分析: https://www.eclipse.org/mat/");
        System.out.println("  4. 检查GC日志: -Xlog:gc*:file=gc.log");
    }
    
    private static String toMB(long bytes) {
        return String.format("%.2f", bytes / 1024.0 / 1024.0);
    }
    
    public static void main(String[] args) {
        registerOOMHandler();
        
        System.out.println("OOM处理器已注册");
        System.out.println("模拟OOM将在3秒后开始...\n");
        
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        
        // 模拟OOM
        List<byte[]> list = new ArrayList<>();
        while (true) {
            list.add(new byte[1024 * 1024]);
        }
    }
}

五、总结与最佳实践

核心要点回顾

区域 作用 线程 异常类型
程序计数器 字节码行号 私有
虚拟机栈 方法调用栈 私有 StackOverflowError、OOM
本地方法栈 Native方法 私有 StackOverflowError、OOM
Java堆 对象存储 共享 OutOfMemoryError
方法区 类信息、常量 共享 OutOfMemoryError
直接内存 NIO缓冲 共享 OutOfMemoryError

最佳实践

  1. 合理设置内存参数

    bash 复制代码
    # 生产环境推荐
    java -Xms4g -Xmx4g \          # 初始和最大堆相同,避免动态扩展
         -Xmn2g \                  # 新生代大小
         -XX:MetaspaceSize=256m \  # 元空间初始大小
         -XX:MaxMetaspaceSize=512m \ # 元空间最大大小
         -XX:+UseG1GC \            # 使用G1垃圾收集器
         -XX:MaxDirectMemorySize=1g \ # 直接内存限制
         -Xlog:gc*:file=gc.log     # GC日志
         -jar app.jar
  2. 避免内存泄漏

    • 及时关闭资源(try-with-resources)
    • 清理ThreadLocal
    • 注销监听器
    • 限制缓存大小
  3. 监控内存使用

    • 定期检查内存使用率
    • 设置内存告警阈值
    • 保留GC日志用于分析
  4. 栈深度优化

    • 避免过深的递归调用
    • 可适当增大-Xss参数
    • 考虑使用迭代替代递归
  5. 直接内存使用

    • 大文件IO使用DirectBuffer
    • 及时释放直接内存
    • 监控直接内存使用量

常见问题排查

问题现象 可能原因 排查方法
StackOverflowError 递归太深 检查代码逻辑,增大-Xss
Java heap space OOM 内存不足或泄漏 jmap分析,MAT诊断
Metaspace OOM 类加载过多 检查动态代理,增大元空间
Direct buffer OOM NIO使用过多 增大MaxDirectMemorySize
GC overhead 频繁GC效果差 增大堆内存,优化对象

扩展阅读

相关推荐
wzhidev2 小时前
04、Python核心数据类型详解:从一段诡异的调试说起
开发语言·python
luanma1509802 小时前
Laravel 7.X核心特性深度解析
android·开发语言·php·lua·laravel
Fortune792 小时前
用Pandas处理时间序列数据(Time Series)
jvm·数据库·python
@haihi2 小时前
ESP32 MQTT示例解析
开发语言·网络·mqtt·github·esp32
2401_878530212 小时前
高级爬虫技巧:处理JavaScript渲染(Selenium)
jvm·数据库·python
2401_873544922 小时前
使用Black自动格式化你的Python代码
jvm·数据库·python
艾莉丝努力练剑2 小时前
【MYSQL】MYSQL学习的一大重点:表的约束
linux·运维·服务器·开发语言·数据库·学习·mysql
Fortune792 小时前
用Python破解简单的替换密码
jvm·数据库·python
Sunshine for you2 小时前
高性能压缩库实现
开发语言·c++·算法