简述

在开始学习java的时候,我们知道java是一个跨平台的语言,为什么java能够跨平台,主要是因为jvm屏蔽了操作系统底层的差异。下面重点来研究下jvm。本次研究的jvm都是jdk1.8的jvm

jvm整体结构主要包括三大部分。
一部分是类装载子系统。这一部分在jvm调优一的时候说过了,
一部分是jvm的运行时数据区,也就是常说的jvm内存模型,是接下来需要重点研究的。
另一部分是字节码的执行引擎,这部分是C++实现的,暂时不去研究。

JVM整体结构及内存模型

下面这张图是jdk1.8 的jvm内存模型示意图。

jvm的内存模型主要包括:堆、栈、本地方法栈、方法区、程序计数器。这几部分都是一块块的内存区域。

栈也可以叫做线程栈,用来存放局部变量等信息,这样说会比较笼统,下面会详细说明。
比如说有个线程执行Main方法,那么在总的栈空间会分配一小块空间给这个线程。那么这就叫线程栈。如果又有一个线程开始执行,那么总的栈空间又会分配一小块空间给线程2。如上图的Main线程和线程2.
栈帧
每个线程栈里面又会栈帧的概念。什么是栈帧。下面通过这个案例说明

比如运行上面的main方法。栈空间分配一个线程栈。执行了main方法。就会在线程栈中分配一个main方法的栈帧(一块内存空间)。在main方法中调用了compute()方法。又分配一个compute的栈帧。简单的来说就是一个方法对应一块栈帧内存区域。也可以这么去理解,因为局部变量只在当前方法中生效,那么每个方法必须要有一个特定的区域将方法的变量保存起来,那么这个区域就是栈帧。
如果compute方法中还调用了方法1、方法2、方法3。那么会在computer栈帧上方继续创建栈帧,而不是在conpute方法的栈帧里面创建栈帧。如下图。整个线程栈和数据结构–栈(FILO)相同,遵循先进后出的概念。

这也好理解。main方法中调computer方法,compute方法调方法1、2、3。方法1、2、3出栈后compute才能出栈。compute出栈main才能出栈。出栈就表示方法执行结束。
栈帧保存了那些内容
每一个栈帧都保存了:局部变量表、操作数栈、动态链接、方法出口。下面一一解释这些概念
局部变量表:存放了编译期可知的各种基本数据类型(boolean byte char short int float long double) 、对象引用(不同于对象本身,可能指向对象起始地址的引用指针,也可能指向一个代表对象的句柄等) 、returnAddress类型(指向了一条字节码指令的地址)。
局部变量表所需的内存空间是在编译期完成分配的,当进入一个方法时,这个方法需要在帧中分配多大的局部变量空间是完全正确的,在方法运行期间不会改变局部变量表的大小。
操作数栈:每个操作数进行操作的时候都需要有一个临时的内存空间,比如说compute方法里面的a=1、b=2、c = (a + b) * 10等这些进行运算的时候就需要放到操作数栈进行运算,或者赋值的时候也是先放入到操作数栈,最后在保存到局部变量表。也就是说操作数栈是一个中转站,临时操作的一块空间。
动态链接:动态链接在jvm调优一有说过,这里再说一遍。比如说上图里面的math.compute();compute()就是一个符号引用,保存在常量池里面(这个常量池后续再说,先这么去理解)。调用到compute()方法时,就要去解析compute(),把符号引用解析成直接引用,获取compute()里面的变量、程序执行打的代码等等在jvm的内存地址。简单说就是解析compute(),获取compute()方法里面需要执行代码的内存地址。动态链接保存的就是方法的内存地址。
方法出口:比如说math.compute();执行完成后会回到main方法里面。那么compute();执行完后怎么知道要回到main方法呢,这就是方法出口保存的内容。方法出口记录了方法执行结束后会返回到哪个程序,记录了那个程序的内存地址

注意:如果栈帧当中局部变量表保存的是一个对象,比如上面的main这个栈帧,它的局部变量表就有math这个对象,但是它保存的并不是真正的math对象,而是math对象的内存地址(这个内存地址就是堆的内存地址,后面会说)。
栈会抛出两种异常:

  1. 如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常
  2. 如果虚拟机可以动态扩展(当前大部分的java虚拟机都允许动态扩展),如果扩展是无法申请到足够的内存,就会抛出OutOfMemoryError异常

总结:栈是线程私有的,它的生命周期和线程相同。栈描述的是Java方法执行的内存模型,每个方法在执行的同时都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直到执行完成的过程,就对应着一个栈帧在线程栈中入栈到出栈的过程。

程序计数器

程序计数器是一块较小的内存空间,它线程私有的内存。可以看做是当前线程所执行的字节码的行号执行器,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。由于Java虚拟机的多线程是通过轮流切换并分配处理器执行时间的方式实现的,为了线程切换后能恢复到正确的执行位置,每条线程都需要一个独立的程序计数器。

方法区(元空间)

方法区是所有线程共享的内存区域,用于存储已被jvm加载的类信息常量静态变量即时编译期编译后的代码等数据。
运行时常量池:是方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池,用于存放编译期生成的各种字面量和符号引用。
如果静态变量是一个对象比如public static User user = new User();那么方法区保存的是user对象在堆的内存地址。
先简单理解后续会专门讲解。像类信息这些它的部分概念是C++的概念,放到最后在理解。

本地方法栈

本地方法就是那些 native方法,这些本地方法是C++实现的。这些本地方法执行也需要有内存空间去存放相关信息,那么本地方法栈就是保存本地方法执行的相关信息。目前来说本地方法栈很少用到。

堆,被所有线程共享的一块内存区域,在jvm启动时创建。此内存区域唯一的目的就是存放对象实例。在java虚拟机规范中描述是:所有的对象实例以及数组都要在堆上分配。
堆细分为新生代和老年代 ,新生代进一步分为Eden空间、From Survivor空间、 To Survivor空间。
根据java虚拟机规范规定:java堆可以处于物理上不连续的内存空间中,只要逻辑上是连续即可如果在堆中没有内存完成实例分配,并且堆也无法在扩展时,将会抛出OutOfMemoryError异常。

在下一篇博客中会专门研究,对象在堆中的内存分配。

JVM内存参数设置


Spring Boot程序的JVM参数设置格式(Tomcat启动直接加在bin目录下catalina.sh文件里):

java -Xms2048M -Xmx2048M -Xmn1024M -Xss512K -XX:MetaspaceSize=256M -XX:MaxMetaspaceSize=256M ‐jar xxx.jar

关于元空间的JVM参数有两个:-XX:MetaspaceSize=N和 -XX:MaxMetaspaceSize=N
-XX:MaxMetaspaceSize: 设置元空间最大值, 默认是-1, 即不限制, 或者说只受限于本地内存大小。
-XX:MetaspaceSize: 指定元空间触发Fullgc的初始阈值(元空间无固定初始大小), 以字节为单位,默认是21M,达到该值就会触发 full gc, 同时收集器会对该值进行调整: 如果释放了大量的空间, 就适当降低该值; 如果释放了很少的空间, 那么在不超过-XX:MaxMetaspaceSize(如果设置了的话) 的情况下, 适当提高该值。这个跟早期jdk版本的-XX:PermSize参数意思不一样.
-XX:PermSize:代表永久代的初始容量。
由于调整元空间的大小需要Full GC,这是非常昂贵的操作,如果应用在启动的时候发生大量Full GC,通常都是由于永久代或元空间发生 了大小调整,基于这种情况,一般建议在JVM参数中将MetaspaceSize和MaxMetaspaceSize设置成一样的值,并设置得比初始值要大, 对于8G物理内存的机器来说,一般我会将这两个值都设置为256M。
-Xss:单个线程栈的大小。设置越小说明一个线程栈里能分配的栈帧就越少,但是对JVM整体来说能开启的线程数会更多

这只是简单的配置。后续在专门写关于jvm调优实战的博客

jvm调优二:jvm内存模型剖析和参数设置相关推荐

  1. JVM内存模型、相关参数设置与命令查看

    JVM内存模型.相关参数设置与命令查看 JVM内存模型,你看这一篇就够了 - 知乎 (zhihu.com) Java虚拟机-Java8内存模型JVM(整理版) - 牧梦者 - 博客园 (cnblogs ...

  2. 【JVM调优】JVM内存管理调优浅谈

    什么是JVM Java Virtual Machine,Java虚拟机 Java虚拟机有自己完善的硬件架构,如处理器.堆栈等,还具有相应的指令系统. Java虚拟机本质上就是一个程序,当它在命令行上启 ...

  3. JVM 调优实战--JVM的运行参数及jinfo查看运行参数信息

    为什么要进行JVM优化? 本篇博文基于jdk1.8来讲解. JVM的参数 标准参数比较稳定,基本上各个不同的jdk版本都会支持. -X参数是非标参数,各个版本不同,可能用着用着就没了. -XX参数也属 ...

  4. JVM调优专题-JVM调优参数

    什么是虚拟机参数配置 在虚拟机运行的过程中,如果可以跟踪系统的运行状态,那么对于问题的故障排查会有一定的帮助,为此,在虚拟机提供了一些跟踪系统状态的参数,使用给定的参数执行Java虚拟机,就可以在系统 ...

  5. 【JVM调优】JVM的运行参数

    在jvm中有很多的参数可以进行设置,这样可以让jvm在各种环境中都能高效的运行,绝大部分的参数保持默认即可 三种参数类型 jvm的参数类型分为三类,分别是 * 标准参数* -help* -versio ...

  6. JVM调优专题-JVM调试工具

    目录 JVM图形化监控和调优工具 JConsole JvisualVm Jvmstat YourKit JVM命令行监控工具 jps(JVM Process Status Tool) jstack j ...

  7. JVM调优:JVM内存分代模型

  8. JVM调优_堆内存溢出和非堆内存溢出

    文章目录 1. pom 2. MemoryController 3. User 对象 4. 动态生成class文件工具类 5. 启动项目 6. 测试连接 7. 异常信息 1. pom <!--动 ...

  9. JVM调优(二)垃圾回收算法

    原文出处: pengjiaheng 可以从不同的的角度去划分垃圾回收算法: 按照基本回收策略分 引用计数(Reference Counting): 比较古老的回收算法.原理是此对象有一个引用,即增加一 ...

最新文章

  1. pandas使用read_csv读取数据使用skiprows参数跳过指定的数据行但保留表头、pandas使用to_csv函数将dataframe保存为gzip压缩文件
  2. 一文看懂Python多进程与多线程编程(工作学习面试必读)
  3. iOS基础知识点总结
  4. head first servlet jsp 学习笔记
  5. 使用一些可选的将字符串配置属性转换为其他类型
  6. 使用TestNG的弹簧测试支持
  7. 编写Play 2的模块,第1部分:使工作正常
  8. linux 文件拆分 合并,Linux下文件的切分与合并的简单方法
  9. 没有bug队——加贝——Python 49,50
  10. 中国制造强在哪儿?从美特斯邦威到Shein
  11. reduce 阶段遍历对象添加到ArrayList中的问题
  12. 防止浏览器拦截的新窗口打开链接方案
  13. python批量修改文件名_python实现批量改文件名称的方法
  14. android 读取文件内容,Android读写文件 获取文件并读取写入数据
  15. android布局时长分析,Android性能优化:布局优化 详细解析(含、、讲解 )
  16. 阿里云服务器定时跑python_通过python调用阿里api定时备份阿里云RDS
  17. 带分销小程序商城源码,完整代码分享
  18. IE浏览器不能自动显示PDF文件的解决办法
  19. 江西科技师范大学泰豪校区计算机专业,扎心了!比取经还艰难的大学路,南昌居然占了这么多……...
  20. 程 | 深度学习 + OpenCV,Python 实现实时视频目标检测 机器之心 09-21

热门文章

  1. 单片机触发器或非门工作原理以及用途
  2. dingdang robot:一个开源的中文智能音箱项目
  3. java过滤关键字,敏感词汇
  4. 【51nod】---1278 相离的圆(二分排序)
  5. Java之消息推送浅入浅出
  6. 前端异步请求async/await,axios的错误用法
  7. 使用命令行检查python语法错误和Debug
  8. shell中各种括号的作用详解()、(())、[]、[[]]、{}(推荐)
  9. AjaxFileUpload组件结合Struts2异步图片上传
  10. uva1203 - Argu