读书笔记之《实战Java虚拟机》(2):认识Java虚拟机的基本结构

300 阅读3分钟

基本结构

虚拟机结构

  1. 类加载子系统
    负责从文件系统,或者网络中加载Class信息;
  2. 方法区
    存放加载的Class信息,和运行时常量;
  3. Java堆
    在虚拟机启动时建立,是Java程序最主要的内存工作区域,几户所有对象实例都存放在堆中,线程共享;
  4. 直接内存
    NIO允许Java直接使用系统内存,相比Java堆访问速度更快,适用于读写频繁场景;
  5. 垃圾回收系统
    对方法区、Java堆和直接内存进行回收,主要是Java堆。垃圾回收系统会在程序后台运行,查找、标识并释放垃圾对象;
  6. Java栈
    每个线程都有一个私有的Java栈,保存着线程局部变量、方法参数,同时和Java方法的调用、返回密切相关;
  7. 本地方法栈
    与Java栈结构类似,不同则是用于本地方法调用,Java虚拟机允许Java直接调用本地方法(通常使用C编写);
  8. PC寄存器
    Program Counter,程序计数器,线程私有,指向当前线程正在执行的方法,如果是本地方法,PC寄存器的值就是undefined;
  9. 执行引擎
    负责执行虚拟机的字节码;

Java堆

垃圾回收机制不同,Java堆结构也可能不同,最常见的结构如下:

堆结构

将整个Java堆分为新生代老年代,详细信息可参考后续垃圾回收机制。

-Xmx,系统最大对空间。例-Xmx64m,指系统最大可用堆空间大小为64m。

例:获取当前系统最大可用堆内存

public static void main(String[] args) {
    long maxMemory = Runtime.getRuntime().maxMemory() / 1024 / 1024;
    System.out.println("-Xmx" + maxMemory + "m");
}

Java栈

同数据结构中栈的定义一样,先进后出。Java栈主要内容为栈帧,每个方法调用,就有有一个对应的栈帧入栈,调用结束对应栈帧出栈。栈帧主要包含局部变量表、操作数栈和帧数据。

栈帧和方法调用

-Xss,系统最大栈空间。例-Xss256k,指系统最大可用栈空间大小为256k。

例:获取系统最大栈深度(通过调整栈大小、局部变量观察输出结果)

public class Test {

    private int count = 0;

    public void recursion() {
        long a = 1L, b = 2, c = 3, d = 4, e = 5, f = 6;
        count++;
        recursion();
    }

    public static void main(String[] args) {
        Test test = new Test();
        try {
            test.recursion();
        } catch (Throwable e) {
            System.out.println(test.count);
            e.printStackTrace();
        }
    }
}

方法区

在JDK 1.6和JDK 1.7中,方法区又称作永久区(Perm),保存系统的类信息,包括类的字段、方法、常量池等。

-XX:PermSize,-XX:MaxPermSize指定初始永久区大小,和最大永久区大小。

例:试探创建类的数量上限(依赖第三方库cglib)

public class Test {

    public static void main(String[] args) {
        int count = 0;
        try {
            while (true) {
                Enhancer enhancer = new Enhancer();
                // 指定不适用缓存
                enhancer.setUseCache(false);
                enhancer.setSuperclass(Test.class);
                enhancer.setCallback(new MethodInterceptor() {
                    @Override
                    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                        return null;
                    }
                });
                enhancer.create();
                count++;
            }
        } catch (Exception e) {
            System.out.println(count);
            e.printStackTrace();
        }
    }
}

JDK 1.8中完全移除了永久区,取而代之的是元数据区,使用参数-XX:MaxMetaspaceSize指定,位于系统内存。