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