1、内存模型
-
程序计数器:指向当前线程所执行的字节码的行号指示器 (JVM的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器只能执行一条线程中的指令,因此,为了线程切换之后能恢复到正常的执行位置,每条线程都需要一个独立的程序计数器,互不影响,独立存储)
-
Java虚拟机栈:线程私有的,生命周期随着线程的生而生,随着线程的死亡而死亡。存储当前线程运行方法时所需要的额数据、指令、返回地址。每个方法执行时候,都会创建一个栈帧
-
本地方法栈:为虚拟机使用到的Native方法服务
-
堆:线程共享的一块内存区域,存放实例对象,几乎所有的对象实例都在这里分配内存。java堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可,就像我们磁盘空间一样。(不过在实现中既可以大小固定,也可以是可扩展,通过-Xmx 和-Xms控制),如果在堆中没有内存完成实例分配,并且堆也无法再扩展时,将会抛出OutOfMemoryError异
-
方法区:方法区和堆一样,是各个线程共享的区域,用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码数据等 ps:方法区中还包括运行时常量池(Runtime Constant Pool),Class文件除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池(Constant Pool Tabel),用于存放编译期生成的各种字面量常量和符号引用,这部分内容将在类加载后进入方法区的运行时常量中存放,当常量池无法再申请到内存时也会抛出OutOfMemoryError异常 ps:字面量:指由字母、数字等构成的字符串或者数值常量;符号引用:是相对于直接引用来说的。主要包括了以下三类常量:类和接口的全限定名、字段的名称和描述符、方法的名称和描述符;只有到运行时被加载到内存后,这些符号才有对应的内存地址信息,这些常量池一旦被装入内存就变成运行时常量池,对应的符号引用在程序加载或运行时会被转变为被加载到内存区域的代码的直接引用,也就是我们说的动态链接了。例如,compute()这个符号引用在运行时就会被转变为compute()方法具体代码在内存中的地址,主要通过对象头里的类型指针去转换直接引用
2、JVM参数
- Xmx:指定 jvm 的最大 heap 大小
- Xms:指定 jvm 的最小 heap 大小 ,高并发应用, 建议和-Xmx一样, 防止因为内存收缩/突然增大带来的性能影响。
- Xmn:指定 jvm 中 New Generation (年轻代)的大小 , 如 :-Xmn256m。 这个参数很影响性能, 如果你的程序需要比较多的临时内存, 建议设置到512M
- Xss:指定线程桟大小 , 如 :-Xss128k, 一般来说,webx框架下的应用需要256K。 如果你的程序有大规模的递归行为, 请考虑设置到512K/1M
- XX:MaxMetaspaceSize: 设置元空间最大值, 默认是-1, 即不限制, 或者说只受限于本地内存大小。
- XX:MetaspaceSize: 指定元空间触发Fullgc的初始阈值(元空间无固定初始大小), 以字节为单位,默认是21M,达到该值就会触发full gc进行类型卸载, 同时收集器会对该值进行调整: 如果释放了大量的空间, 就适当降低该值
3、案例分析
通常我们会对系统进行一个评估计算,得到峰值时大概会产生多少对象,结合服务的数量,服务器的配置大小,进行参数设置(熟悉系统的整个运行流程)