s1.堆内存(Heap Memory)
堆是Java虚拟机最大的内存区域,也是Java虚拟机管理的内存中最重要的一部分,用于存储对象实例和数组对象。所有的Java对象都在Java堆中进行分配。Java堆是线程共享的,当堆中没有足够的内存分配新的对象时,Java虚拟机就会触发垃圾回收(Garbage Collection),清除堆中不用的对象腾出空间供其他对象分配。Java堆中的内存分配是顺序式的,即先进先出。堆内存分为新生代,老年代和永久代(方法区,在JDK8及以后的版本中,已经被元空间所代替)。Java的堆内存结构可以看作是由新生代和老年代两部分组成的。
s1.1.新生代(Young Generation)
新生代是指存放新创建的对象的内存区域,包括Eden区和两个Survivor区。其中,所有新创建的对象都会被分配到Eden区。当Eden区满时,Java虚拟机会对Eden区中的对象进行垃圾回收,将还有用的对象移动到Survivor区,而那些无用的对象则会被清理掉。在两个Survivor区中,总有一个是空的,Java虚拟机会将从Eden区和上次未清理的Survivor区中还有用的对象移动到空的Survivor区中,并按年龄递增进行标记,最终达到老年代阈值,即允许直接进入到老年代中。
s1.2.老年代(Old Generation)
用于存放较久存活的对象实例。当Eden区、两个Survivor区无法保存新对象时,新对象会被直接分配到老年代。这些对象往往是由多次垃圾回收器的清理操作所保留下来的。由于对象存活时间比较长,因此老年代的容量相对较小,也比较稳定。
s2.栈内存(Stack)
是Java虚拟机所管理的一块内存区域,包括虚拟机栈和本地方法栈,栈内存完全由栈帧(Stack Capacity)组成是,一种先进后出(LIFO)的数据结构。用于存储线程执行方法的局部变量表、操作数栈、方法出口等信息。每个线程都有自己的栈空间,用于支持线程的并发执行。
S2.1.虚拟机栈(Java Virtual Machine Stacks)
Java虚拟机栈作为每个线程的私有内存区域,默认情况下线程占用栈的内存大小是固定的,被称为栈容量(Stack Capacity)。Java虚拟机栈主要存储局部变量、操作数栈、方法出口等信息。在java中,当线程执行一个方法时,Java虚拟机栈将会动态的创建一个栈帧(Stack Frame),它用于存储该方法的局部变量、操作数栈和返回地址等。
s2.2.本地方法栈(Native Method Stack)
本地方法栈和Java虚拟机栈类似,它为Java虚拟机提供Native方法(Java调用C/C++函数)的支持。由于Java虚拟机使用C语言方法调用约定来执行Native方法,在执行Native方法时,本地方法栈也被使用了。
S2.3.栈帧(Stack Frame)
栈内存可以看做是由若干个栈帧(Stack Frame)组成,每个栈帧对应着一个方法的执行过程。当一个方法被调用时,Java虚拟机就会为这个方法创建一个新的栈帧,并把它压入栈内存的顶端。当这个方法执行结束时,栈帧就会被弹出,释放该方法使用的内存空间,然后返回上一层调用方法的位置继续执行。每个栈帧由四个主要部分组成局部变量表(Local Variable Table)、操作数栈(Operand Stack)、方法返回值和方法返回地址
S2.3.1.局部变量表(Local Variable Table)
用于存放方法中的局部变量,包括基本数据类型和对象的引用。
S2.3.2.操作数栈(Operand Stack)
用于存放方法的操作数,包括方法参数和临时变量。
S2.3.3.方法返回值
用于存储方法的返回值,可以是基本类型和对象的引用。
S2.3.4.方法返回地址
用于返回到调用该方法的地址。当方法执行完成后,Java虚拟机会跳转到该地址,继续执行下一步操作。
S3.方法区(Method Area)/元空间(MetaSpace)
方法区存储着类信息、常量、静态变量、即时编译器编译后的代码等。方法区属于堆内存的一部分,在一个成为永久代的内存区域,也是线程共享的。当启动Java虚拟机时,方法区将被初始化并占用一定的初始容量。随着类加载的进行,方法区的容量将随之扩大,大量使用反射和字节码技术的应用可能会导致方法区的容量耗尽而导致OutOfMemoryError。在JDK8及以后的版本中,永久代已经被元空间(MetaSpace)所代替,元空间使用的是本地内存(Native Memory)来实现的,并且允许整个堆的物理大小超过了本地内存的限制。
S3.1. 运行时常量池
包含了一些常量表达式的字面值和符号引用,例如字符串常量、类和方法的名称和描述符等等。在类加载的过程中,字节码文件的常量池会被解析并存储到运行时常量池中。
S3.2. 类信息
方法区还包含了类的相关信息,例如类的名称、父类、接口、字段、方法等。这些信息会在类加载器将类加载到内存中时被存储到方法区中。
S3.3. 静态变量
静态变量指类中使用static修饰的变量,它们被存储在方法区的静态变量表中。当JVM中的某个类被载入内存时,其中所有静态变量都会被初始化并存储到静态变量表中。
S3.4. 方法信息
方法区中还存储了类的方法信息,包括方法的名称、参数类型、返回值类型、方法的访问修饰符等。它们都被存储在方法表中。
S3.5. 动态生成的代理类和匿名类
方法区中还可能存储动态生成的代理类和匿名类的相关信息。
S4.程序计数器(Program Counter Register)
程序计数器是一块比较小的内存区域,它可以看作是当前线程所执行字节码的行号指示器,也就是用于存储当前方法的地址。每个线程都有一个独立的程序计数器,当线程被创建时,程序计数器为空。当线程执行一个方法时,程序计数器将被改变为当前方法的行号指针。当线程执行完毕或者暂停时,程序计数器将指向下一个要执行的指令。