Java虚拟机内存模型:

程序计数器、虚拟机栈、本地方法栈、Java堆、元空间(方法区),如图:

Java 虚拟机栈的作用 java虚拟机栈动态扩展_JVM参数

程序计数器
1、线程私有,用于记录下一条运行的指令(如:当线程CPU时间用完,要用计数器记录运行到哪了,重新获取CPU后可以从该条命令开始)
2、当前线程在执行Java方法的时候,计数器记录正在执行的Java字节码地址,如果是Native方法,则计数器为空

Java虚拟机栈
1、线程私有空间,他和Java线程一同创建,保存方法的局部变量,部分结果,参与方法调用和返回
2、Java栈大小可以动态或者固定,定义两种栈空间异常:StackOverflowError(请求的栈深度大于最大栈深度)、OutOfMemoryError(栈动态扩展,,扩展过程中,没有足够内存支持扩展)
3、-Xss设置栈空间最大值(如-Xss2M,最大为两兆)
4、栈帧(数据结构,保存上下文数据),其中局部变量表根据方法参数和局部变量增多,局部变量表会变大,栈帧会出现膨结构如下图:
5、栈帧中与调优关系最紧密的部分是局部变量表。局部变量表主要存储方法参数和局部变量。局部变量表以“字”为单位,一个字32位长度。long和double占两个字,其余都是一个字。对于非static方法,还会传当前对象(tihs)作为参数(jclasslib工具可以查看Class类文件结构)
6、字空间可以重用,而且GC可以引用到局部变量表的字,GC无法回收局部变量表的字,需要回收的时候可以显示 复制位null,如果那些比较大的局部变量表字没有被后续代码重用,它所引用的对象不会被GC回收。
7、函数调用嵌套的次数由栈大小决定。栈大,函数嵌套调用次数则大。对于一个函数而言,参数,局部变量越多,它的栈帧就越大,嵌套调用次数就越少。

Java 虚拟机栈的作用 java虚拟机栈动态扩展_内存模型_02

本地方法栈
1、调用本地方法,调用时进入一个不受JVM约束的状态。本地方法一般是用C实现的
2、线程私有,本地方法通过JNI来访问虚拟机运行时的数据区,甚至可以调用寄存器,具有和JVM相同的能力。
这里介绍一些JNI(Java Native Interface):JNI使Java深度使用操作系统的特性功能,复用非Java代码。如果大量使用JNI就会丧失跨平台的特性,威胁程序运行的稳定性。如果需要与本地代码交互,最好用中间标准框架进行解藕,这样本地方法崩溃也不会影响到JVM的稳定。在要求极高执行效率、偏底层的跨进程操作的时候,可以设计为JNI调用方式。

Java堆
1、线程共享,几乎所有对象和数组都是在堆中分配空间
2、分为新生代和老年代两个部分,刚创建的对象在新生代中,生存很久,没有被回收的对象会移入老年代
3、新生代可以分为:eden、survivor space0()、survivor space1(),刚建立的对象会在eden,经历一次回收后幸存的对象会存入survivor空间,当survivor space0() 满了后,就行把仍存活的对象复制到survivor space1,然后清空survivor space0空间

方法区(目前最新称为元空间):(最新中,字符串常量已经移动到了堆内存)
1、线程共享,主要保存类的元数据。类的类型信息、常量池、域信息、方法信息。
2、类型信息包括:类的完整名称,父类完整名称、类型修饰符和类型的直接接口类表
3、也称为永久区,主要存放常量和类的定义信息,把部分信息来自class文件。
4、GC对其回收分为两类:常量池的回收、元数据的回收。常量池中常量没有被引用就会被回收(如一个String对象);对于元数据,当虚拟机取人这个类信息没有并且不会再被使用,就会去回收(事实上当该类的所有实例以及被回收,而且加载该类的ClassLoader已经被回收,GC就由可能回收该类型)

JVM内存分配参数:
1、设置最大堆内存:-Xmx ;指的是新生代和老年代之和,程序运行的时候可以用Runtime.getRunTime().maxMemory()取得系统可用的最大堆内存
2、设置最小堆内存:-Xms;也就是JVM启动时,所占的操作系统内存大小,程序一开始运行时先向操作系统获取该大小,而且尽量按这个内存运行程序,当-Xms内存实在不够时,才向操作系统申请更多的内存。最大为-Xmx。如果该值太小,会频繁触发GC,影响程序。可通过:-Xmx11M-Xms11M-verbose:gc运行(最大堆内存和最小堆内存一致,能有效减少GC次数)
3、设置新生代:-Xmn,新生代的大小会影响老年代大小,一般设置占堆的1/4–1/3;-XX:NewSize和-XX:MaxNewSize可以设置初始和最大的新生代大小,但是这样设置可能会导致内存震荡,产生不必要的系统开销
4、设置持久代:-XX:MaxPermSize,-XX:PermSize设置持久代最大值和初始值,持久代大小直接决定了系统内支持多少个类和多少常量(一般来说最大值64M或128M即可满足绝大部分应用程序)
5、设置线程栈:-Xss,栈空间过小,限制函数调用的深度;过大,导致开设线程需要的内存成本上升,线程总数下降。堆内存等内存过大,也可能导致操作系统用于支持线程栈的内存减少。
6、堆比例分配:-XX:SurvivorRatio=n设置新生代eden空间和s0空间比例关系;-XX:NewRatio=n设置老年代和新生代的比例
7、查看内存情况:-XX:+PrintGCDetails参数打印堆的实际大小。

堆分配参数总结:
1、-Xms:设置程序启动时初始堆大小
2、-Xmx:设置程序能获得最大堆大小
3、-Xss:设置线程栈大小
4、-XX:MinHeapFreeRatio:设置堆空间最小空闲比例,空闲内存比这小时,JVM会扩展堆空间
5、-XX:MaxHeapFreeRatio:设置堆空间的最大空闲比例,大于该值时,会压缩堆空间
6、-XX:TargetSurvivorRatio:设置survivor区的可使用率。当survivor区空间使用率达到该数值时,对象会进入老年代。
7、-XX:NewSize : 设置年轻代大小
8、-XX:NewRatio : 设置老年代与新生代比例,
9、-XX:SurvivorRatio:设置新生代eden区和survivor区的比例
10、-XX:MaxPermSize:设置最大永久区大小
11、-XX:PermSize:设置永久区初始值
12、-XX:TargetSurvivorRatio:设置survivor区的可使用率。