jvm内存结构
jvm内存结构分为五个组成部分,分别为程序计数器、虚拟机栈、本地方法栈、堆以及方法区,其中程序计数器、虚拟机栈和本地方法栈是线程私有的。
程序计数器(Program Counter Register)
程序计数器是一块很小的内存空间,它是线程私有的,可以认为是线程的行号指示器。程序计数器的作用就是记住下一条jvm指令的执行地址,他在物理上是通过寄存器来实现的。
它有两个特点:
- 线程私有的
- 不存在内存溢出问题
虚拟机栈(Java Virtual Machine Stacks)
虚拟机栈就是平时我们所说的栈,它是线程运行需要的内存空间。它的基础组成是栈帧(Frame),栈帧是每个方法运行时需要的内存。每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法。
垃圾回收不会涉及到栈内存,在每次方法调用完成之后会销毁对应的栈帧,所以它不会也不需要垃圾回收。栈内存并不是设置越大越好,栈内存划分越大并不会增强程序的性能,反而会减少线程的数量。
栈是有可能会内存溢出的,在栈帧过大或者栈帧过多的情况下,栈内存会溢出。
public static void main(String[] args) {
try {
method1();
} catch (Exception e) {
e.printStackTrace();
}
}
private static void method1() {
method1();
}
例如一个无限调用自己的方法就会导致栈帧数量越来越多,最终导致内存溢出。
本地方法栈
本地方法栈和虚拟机栈作用类似,区别是本地方法栈管理的是虚拟机使用到的native方法服务,例如底层调用的c或者c++代码。
堆(Heap)
通过new关键字,创建的对象都会使用堆内存。它有两个特点:
- 它是线程共有的,堆中对象需要考虑线程安全问题
- 堆受到垃圾回收机制的管理
堆会存在内存溢出问题,虽然它受到垃圾回收机制的管理,但是垃圾回收机制管理的是没有引用的对象,如果堆中的对象都有引用,并且还在增加,就可能导致内存溢出问题。
public static void main(String[] args) {
int i= 0;
try {
List<String> list = new ArrayList<>();
String a = "hello";
while (true) {
list.add(a);
a += a; // "hellohello","hellohellohello"...
i++;
}
} catch (Throwable throwable) {
throwable.printStackTrace();
System.out.println(i);
}
}
方法区(Method Area)
The Java Virtual Machine has a method area that is shared among all Java Virtual Machine threads. The method area is analogous to the storage area for compiled code of a conventional language or analogous to the "text" segment in an operating system process. It stores per-class structures such as the run-time constant pool, field and method data, and the code for methods and constructors, including the special methods (§2.9) used in class and instance initialization and interface initialization.
根据java官方文档,方法区是一个线程共享的区域。它存储的是运行时常量池、类信息等。方法区是一种规范,在不同时期有不同的实现。老版本的jdk中,是由永久代来实现的,这样jvm的垃圾回收器就可以像管理堆那样管理这部分区域,但是jdk7之后,方法区是由元空间(Metaspace)来实现的。
可以通过-Xmx100m
这种方式来设置堆内存的大小。