JVM的内存分区(内存模型)
JVM内存主要分为以下几个区域:
-
程序计数器:这是一块较小的内存区域,用于记录当前线程执行的字节码指令地址。每个线程都有自己的程序计数器,互不干扰,且这是JVM内存区域中唯一不会抛出OutOfMemoryError的区域。1
-
Java虚拟机栈:每个线程在创建时会分配一个独立的Java栈,用于存储方法调用的局部变量和方法调用信息。当方法调用时,会创建一个栈帧,方法执行完成后,栈帧会被销毁。1
-
本地方法栈:用于存储本地方法的调用信息,本地方法通常是通过JNI调用的C或C++方法。
-
堆:这是JVM中最大的内存区域,用于存储对象实例和数组。堆被所有线程共享,是垃圾回收的主要对象。堆可以分为新生代和老年代,新生代又分为Eden区和两个Survivor区。1
-
方法区:用于存储类的元信息、静态变量、运行时常量池等。在JDK 8之前,方法区被称为永久代(PermGen),而在JDK 8及以后,则被元空间(Metaspace)取代,并且不再与堆连续,而是存在于本地内存中。1
堆的内存管理机制:
- 新生代:新创建的对象首先放在Eden区,当Eden区满时,会触发Young Garbage Collection(YGC),将存活的对象移动到Survivor区。如果对象在Survivor区经过一定次数的YGC后仍未被回收,则会移动到老年代。
- 老年代:存放生命周期较长的对象。如果新生代无法容纳新对象,可以直接分配到老年代。
- Full Garbage Collection(FGC):当Eden区和老年代都无法存放新对象时,会触发FGC,尝试放在老年代,如果还是容纳不了,则会抛出OOM异常。
方法区的变化:
在JDK 8之前,方法区被称为永久代(PermGen),而在JDK 8及以后,则被元空间(Metaspace)取代。元空间不再与堆连续,而是存在于本地内存中。元空间的大小不受限于JVM分配的内存大小,只受限于机器内存大小,这样可以避免内存溢出。