JVM内存模型
方法区
也称永久代,存放JVM加载的类的信息、静态变量和常量,是线程的共享区域。方法区的大小默认是16MB到64MB,可以通过-XX:PermSize和-XX:MaxPermSize来修改。
运行时常量池是方法区的一部分,用于存储由编译器生成的各种字符常量,当类被加载时,类中的字符常量会被放入方法区的常量池中。
虚拟机栈
描述了Java方法的内存模型,当一个方法被调用时,就会创建一个“栈帧”来保存临时变量表、操作栈和方法出口等信息。方法从被调用到执行完成对应了栈帧的入栈如出栈。生命周期和线程相同,是线程私有的。
临时变量表存放了编译器能够识别的所有基本数据类型和对象引用,其中64位的long和double会占用两个位置。临时变量表所占用的空间是在编译器确定的,所以在运行时栈帧并不会改变其所占用的空间大小。
本地方法栈
和虚拟机栈功能相似,区别在于虚拟机栈是为JVM调用Java方法服务的,而本地方法栈是为调用Native方法服务的。
堆
堆的Java管理的内存中最大的一块内存区域,堆也是线程共享的内存区域,在JVM启动时创建。其大小可以通过-Xms和-Xmx来设置,其中-Xms指定JVM启动时的最小堆内存,其默认为物理内存的1/64,但小于1G,-Xmx指定JVM能够分配的最大堆内存,其默认为物理内存的1/4,但不超过1G。当余堆内存小于40%时,JVM会重新分配对内存为-Xmx指定的大小,当余堆内存大于70%时,JVM会重新分配堆内存为-Xms指定的大小。为了避免频繁的堆内存重新分配,通常会将-Xms和-Xmx设置成相同的值。
现代收集器大多采用分代算法,所以,堆被分为新生代和老年代,新生代存储新创建的对象和还没有进入老年代的对象,老年代存储经过多次新生代GC仍然存活的对象。
新生代由一块Eden Space和两块大小相同的Survivor Space组成,可以通过-Xmn来指定新生代的空间大小。
有时,一个新创建的对象也有可能直接进入老年代,比如大对象和大数组。老年代的空间大小为-Xmx - -Xmn。
程序计数器
程序计数器是最小的一块内存区域,它用来记录当前线程执行的字节码指令的行号。字节码解释器工作时就是改变这个计数器来标记下一条需要被执行的字节码指令。