jvm底层结构(对内存的分门别类)
1、程序计数器(线程私有,记录当前线程字节码指令执行位置)
-
程序计数器为什么是线程私有的?
java程序多为并发执行,多线程之间会争抢cpu资源,获取执行时间,cpu会不停切换线程,所以需要pc寄存器来记录当前线程字节码指令执行位置,以方便切换线程时能找到上次执行的位置。程序计数器为jvm内存中最小的一块内存,运行速度快,无垃圾回收,在字节码指令执行时,由执行引掣维护计数器的标记位置变量
2、方法区(用于存放程序运行时类或所有对象共有的一些变量,如静态变量,常量以及类信息元数据)
1.为什么有堆了还要设计方法区?
堆中对象繁多,一些线程私有或引用对象与共有变量对象区分开,提高系统运行效率
3、虚拟机栈(线程私有)
虚拟机栈为线程私有的一种栈结构(先进先出),而虚拟机栈又由一个一个的栈帧组成,一个方法对应一个栈帧,栈顶的栈帧称为当前栈帧,对应当前正在运行的方法。
1.栈帧的结构?
栈帧由局部变量表,操作数栈,动态链接,方法返回地址以及附加信息组成。
局部变量表:存储方法中变量的存储结构,底层是字节数组,局部变量表的大小在编译期间就会确定,定义在code属性中的localstatcks字段中
操作数栈:操作数栈为方法执行时一块临时内存空间,用于临时存储执行方法时涉及到的变量。类似于超市的储物柜,当执行方法操作时,会将局部变量以及各种操作产生的结果临时存储在操作数栈中,当相应的字节码指令执行完毕后,会进行出栈操作将变量存入局部变量表
动态链接:存储该方法中对其他方法的引用符号,并将引用符号转换为直接引用,类似现实生活中花名册直接找到该名字对应的人,会先根据引用符号找到该引用符在运行时常量池中记录的方法地址,根据该地址直接找到引用方法的具体代码(字节码指令)
方法返回地址:用于记录方法中调用其他方法完毕后在父方法中执行的代码位置
2.局部变量表中会储存对象吗?
可以的,但是如果该对象被其他方法所引用或作为返回值被调用者引用,则对象存放在堆空间中,局部变量表中只会存放该对象在堆内存中的地址(即栈与堆之间会指针映射关系)
4、本地方法区
提供本地c语言方法接口地址
5、堆空间
堆为jvm内存中最大的一块区域,也是gc的主要区域,堆内存按照对象年龄可分为新生代与老年代,而新生代中又分为伊甸区与幸存者区1与2,默认新生代与老年代比例为1:2,伊甸区与幸存者区比例为8:1:1,新对象会先存放在伊甸区,如果伊甸区满则触发minorgc,将存活对象转移至幸存者区1,而后如果继续有新对象,则继续往伊甸区添加,若满则在伊甸区,以及有对象的幸存者区触发minorgc,同时会将对象年龄加1,当分代年龄大于等于15时转移至老年区,根据不同的虚拟机,转移至老年区的分代年龄有所不同,同时若幸存者区一次性接收的对象大小超过幸存者区内存的百分50则会将该批对象直接转移至老年区(这对于朝生夕死型对象来说,是极耗费性能的,调优需要考虑),每发生一次gc(不论minor还是full),都会触发stw(即停止所有用户线程),这是极其影响性能的,同时gc时间还与gc区域大小有关,故minorgc不一定时间很短,具体需要根据区域大小来判定。调优时需要根据具体的各区域对象转移机制,以及接口预估产生对象大小具体数据来分析各区域的内存大小以及比例
常见的调优工具都有哪些?
jvisualvm,arthas(阿里开源调优工具)
arthas: 可以快速查找cpu占用最高的线程,以及阻塞的线程。可以查看线上运行的代码是否为最新更新的代码,可以热替换线上代码等等其他非常方便的功能