# JVM(HotSpot)各个工作区域以及作用
**根据**JVM 8规范,JVM运行时内存共分为虚拟机栈,堆,元空间,程序计数器,本地方法栈5个部分,还有一个部分内存叫直接内存,属于操作系统的本地内存,也是可以直接的操作的。
掌握以下这两张图,很重要。
JMM内存模型:


> ### 1. 元空间(Metaspace)
参数配置:
|参数配置|参数名称|默认大小|
|-------|-------|-------|
|-XX:MetaspaceSize=10m|元空间初始大小|21Mb|
|-XX:MaxMetaspaceSize=20m|元空间最大空间|Win:内存4/1,Linux:接近无穷大,必须配置大小|
元空间的本质和永久代类似,都是对JVM规范中方法区的实现,不过元空间与永久代(方法区/Prem)之间最大的区别在于元:元空间并不在虚拟机当中,而是使用本地内存。当加载class超过Max值的时候会抛出的OutOfMemoryError:MetaSpace。

作用:用于存储已经被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
> ### 2. 虚拟机栈(JVM Stacks)
每个线程都有一个私有的栈,随着线程的创建而创建,栈里面存着的是一种叫“栈帧”的东西,每个方法会创建一个栈帧,并且入栈,一旦完成调用,则出栈。所有的栈帧都出栈后,线程也就完成了使命。栈帧中存放了局部变量表(基本数据类型(八大数据类型)和对象引用)、操作数栈、方法出口等信息。栈的大小可以固定也可以动态扩容。

作用:用于执行java方法,存放局部变量表,操作数栈、方法出口信息、动态链接数据。
> ### 3. 本地方法栈(Native Method Stack)
与虚拟机栈相似,本地方法栈与虚拟机栈所发挥一样的作用,唯一的区别是虚拟机栈执行java方法,本地方法栈执行native方法,例如unsafe类中被native修饰的方法。在虚拟机规范中对本地方法栈中方法使用的语言、使用方法与数据结构没有强制规定,因此虚拟机可以自由实现它。
在HotSpot中本栈和虚拟机栈合二为一,都会抛出
StackOverflowError和OutOfMemoryError异常。
作用:用于执行native 修饰的方法,
> ### 4. 程序计数器(Program Counter Register)
程序计数器可以看成是当前线程所执行的字节码的行号指示器。字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能。
由于java虚拟机的多线程是通过线程轮流切换并且分配处理器执行时间的方式来实现,在任何一个确定的时刻,一个处理器(对于多内核来说是一个内存)都会执行一条线程中的指令。因此,为了线程切换后能恢复到正确的执行位置,每条线程都需要一个独立的程序计数器,各个线程之间计数器互不影响,独立存储,我们称这类内存区域为“线程私有”内存。
如果线程正在执行的是java方法,这个计数器记录的是正在执行的虚拟机字节码指令地址;如果正在执行的是native方法,这个计数器则为空(undefined)。此内存区域是唯一一个在java虚拟机规范中没有任何OOM情况的区域。
> ### 5. 堆内存(Heap)
参数配置:
|参数配置|名称|默认大小|
|-------|-------|-------|
|-Xms10m|初始堆大小|机器内存64/1|
|-Xmx10m|最大堆大小|机器内存的4/1|
|-Xmn5m|初始年轻代大小|初始堆大小的30%|
|-XX:InitialSurvivorRatio=8|设置年轻代中Eden和S0/S1的空间占比|默认8,则e=8,s0=1,s1=1|
|-XX:NewRatio=2|设置堆中年轻代和老年代的空间占比|默认2,Y=1,old=2。假设4,Y=1,Old=4|
|-XX:MaxTenuringThreshold=15|设置垃圾最大代年纪|默认15,设置则15次GC,直接进入老年代|
堆内存是JVM所有线程共享的部分,在虚拟机启动的时候就已经创建,所有的对象和数组都在堆上进行分配。
这部分空间可以通过GC进行回收,是垃圾收集器管理的主要区域,当申请不到空间是会抛OOM,堆是JVM内存占用最大,管理最复杂的一个区域,其唯一的用途就是存放在对象实例:所有的对象实例以及数组都是在堆上进行分配。Jdk1.6以后,字符串常量池从永久代中剥离出来,存放在堆中。
堆可以划分为:新生代和老年代,新生代(Young Generation)中分Eden、From Survivor、To Survivor,而老年代(Old generation)只有老年代。
GC回收算法:年轻代采用复制算法,s0/s1就是用来复制清除的,老年代采用标记整理算法
年轻代GC收集器:Serial(串行)、ParNew(Y并行,Old串行)、Paralle Scavenge(并行)、G1
老年代GC代收集器:CMS(ConcurrentMarkSweep)、Serail Old、Parallel Old、G1
**堆空间内存分配情况(默认情况):**
- 老年代(Young Generation):三分之二的堆空间
- 年轻代(Old Generation):三分之一的堆空间
- eden区:8/10的年轻代空间
- 幸存者1区(From Survivor0):1/10的年轻代空间
- 幸存者2区(To Survivor1):1/10的年轻代空间

作用:所有对象和数组都在堆上分配。
> ### 6. 直接内存(Direct Memory)
参数配置:
|参数配置|名称|默认大小|
|-------|-------|-------|
|-XX:MaxDirectMemorySize=10m|最大直接内存|默认最大堆的大小|
直接内存并不是虚拟机运行时数据区的一部分,也不是java虚拟机规范中定义的内存区域。在JDK1.4中新加入了NIO(New Input/Output)类,引入了一种基于通道(Channel)与缓冲区(Buffer)的I/O方式,它可以使用native函数库直接分配堆外内存,然后通过一个存储在java堆中的DirectByteBuffer对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在java堆和Native堆中来回复制数据。
直接内存的分配不受限于java堆的大小限制,但是默认受限于本机的总内存大小限制(包括RAM以及SWAP区或者分页文件)。RAM是内存,SWAP分区即为交换区,SWAP属于从硬盘中分区出来的,用于在运行RAM内存不够的时候,从RAM内存中一部分空间释放出来,以供运行程序继续时候。被释放的空间数据可能来自很长时间没有什么操作的程序,这些数据会存储在的Swap区,等到这些程序需要的时候再从Swap分区中恢复到RAM当中。
作用:用于IO的的分配,提高IO复制的速度,减少数据从系统内存复制到虚拟机内存的这个多余的复制步骤,而是程序直接操作系统内存,netty在这块体现的淋漓尽致。
> ### 7. 运行时常量池(Runtime Constant Pool)
运行时常量池属于元空间(方法区)的一部分,class文件除了有类的版本、字段、方法、接口、等描述信息外,还包含一项信息是常量池(Constant Pool table),用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存放。
运行时常量池相当于Class文件常量池的另一种重要特性是具备动态性,Class文件常量池不具备动态性。
不要求常量只是编译期间才能产生,运行期间也可以将新的常量池放入池中。这种特性可以通过String.intern()方法实现。
既然属于元空间,自然受限于元空间的大小,当常量池无法申请到内存的时候则会OOM异常。
JVM(HotSpot)各个工作区域以及作用