← 返回首页

JVM内存模型详解:运行时数据区

📂 java ⏱ 3 min 452 words

JVM内存模型详解

JVM运行时数据区

JVM在执行Java程序时会把内存划分为多个数据区域,每个区域有特定的用途。

┌─────────────────────────────────────────┐
│              JVM运行时数据区              │
├──────────────┬──────────────────────────┤
│  线程私有     │        线程共享            │
├──────────────┼──────────────────────────┤
│ 程序计数器    │         堆(Heap)          │
│ 虚拟机栈      │   方法区/元空间            │
│ 本地方法栈    │      运行时常量池          │
└──────────────┴──────────────────────────┘

程序计数器

线程私有的内存区域,记录当前线程执行的字节码行号。

public class Counter {
    public static void main(String[] args) {
        int a = 10;    // 行号 0
        int b = 20;    // 行号 1
        int c = a + b; // 行号 2
        System.out.println(c); // 行号 3
    }
}
// 程序计数器记录当前执行到哪一行字节码

虚拟机栈

线程私有,每个方法执行时创建一个栈帧。

public class StackDemo {
    public static void main(String[] args) {
        methodA();
    }

    public static void methodA() {
        int x = 10;
        methodB(x);
    }

    public static void methodB(int param) {
        String msg = "hello";
        System.out.println(msg);
    }
}
// 栈帧结构:
// methodA的栈帧:[x=10]
// methodB的栈帧:[param=10, msg="hello"]

栈帧结构

┌─────────────────┐
│   局部变量表     │  存放方法参数和局部变量
├─────────────────┤
│   操作数栈       │  方法执行的工作区
├─────────────────┤
│   动态链接       │  指向运行时常量池
├─────────────────┤
│   方法返回地址   │  方法退出后回到调用处
└─────────────────┘

栈溢出(StackOverflowError)

public class StackOverflowDemo {
    public static void main(String[] args) {
        recursiveMethod();
    }

    public static void recursiveMethod() {
        recursiveMethod(); // 无限递归
    }
}
// Exception in thread "main" java.lang.StackOverflowError

堆(Heap)

线程共享,存放对象实例和数组。

public class HeapDemo {
    public static void main(String[] args) {
        // 对象在堆上分配
        User user = new User("张三");
        // 数组在堆上分配
        int[] arr = new int[100];
    }
}

class User {
    private String name; // name引用在栈上,String对象在堆上
    public User(String name) {
        this.name = name;
    }
}

堆内存分代

┌────────────────────────────────────────┐
│                 堆内存                   │
├───────────────────┬────────────────────┤
│    新生代(Young)   │      老年代(Old)    │
├─────┬──────┬──────┤                    │
│Eden │ S0   │ S1   │                    │
│ 8   │ 1    │ 1    │                    │
└─────┴──────┴──────┴────────────────────┘
# 查看堆内存配置
java -XX:+PrintFlagsFinal -version | grep -i heap

# 设置堆大小
java -Xms512m -Xmx1024m MyApp

方法区/元空间

存放类信息、常量、静态变量。

public class MethodAreaDemo {
    // 静态变量存储在方法区
    public static final String CONSTANT = "常量";
    public static int count = 0;

    public static void main(String[] args) {
        // 类信息存储在方法区
        Class<?> clazz = MethodAreaDemo.class;
        System.out.println(clazz.getName());
    }
}

永久代 vs 元空间

特性 永久代(PermGen) 元空间(Metaspace)
位置 JVM内存 本地内存
大小限制 固定大小 可动态扩展
JVM版本 Java 7及以前 Java 8+
# 设置元空间大小
java -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m MyApp

运行时常量池

存放编译期生成的字面量和符号引用。

public class ConstantPoolDemo {
    public static void main(String[] args) {
        String s1 = "hello"; // 字面量,存入常量池
        String s2 = "hello"; // 引用常量池中的"hello"
        String s3 = new String("hello"); // 新建对象

        System.out.println(s1 == s2);       // true
        System.out.println(s1 == s3);       // false
        System.out.println(s1 == s3.intern()); // true
    }
}

直接内存

NIO使用直接内存,绕过JVM堆,提高IO性能。

public class DirectMemoryDemo {
    public static void main(String[] args) {
        // 分配直接内存
        ByteBuffer buffer = ByteBuffer.allocateDirect(1024 * 1024);
        buffer.put((byte) 'A');
        buffer.flip();
        System.out.println((char) buffer.get());
    }
}

内存溢出场景

// 堆溢出
public class HeapOOM {
    public static void main(String[] args) {
        List<byte[]> list = new ArrayList<>();
        while (true) {
            list.add(new byte[1024 * 1024]); // 不断创建大对象
        }
    }
}

// 栈溢出
public class StackOOM {
    public static void main(String[] args) {
        stackOverflow();
    }
    private static void stackOverflow() {
        stackOverflow();
    }
}

总结

理解JVM内存模型是进行性能调优和问题排查的基础。重点关注堆和栈的区别,以及GC对堆内存的管理。