JVM对大家来说应该都不陌生,它的知识点也是我们面试中经常问到的,包括JVM性能调优,堆栈方法区的理解,各有什么不同,虚拟机的参数,垃圾回收等等。
1、我们学习第一步当然是知道它是什么?那下边就介绍一下,什么是JVM?
JVM,就是一台虚拟的机器,一般分为系统虚拟机和程序虚拟机,我们Java中的虚拟机就是程序虚拟机,它专门为执行单个计算机程序而设计,在java虚拟机中执行的指令我们称为java字节码指令。当启动一个Java程序时,就会运行一个Java虚拟机,每个Java程序都会对应一个Java虚拟机,运行中的Java虚拟机,也可以称为Java虚拟机的一个实例。Java程序运行结束关闭后,运行这个程序的Java虚拟机也会关闭。
2、认识JVM虚拟机的基本结构
一般分为3大模块,分别为:
1.类加载子系统: 负责从文件系统或网络中加载Class信息,加载的信息存放在一快称为方法区的内存空间。
2.运行时数据区域:包括堆、栈、方法区等
3.字节码执行引擎:最核心的组件,它辅助执行虚拟机的字节码,一般用户先进行编译成机器码后执行。动态的修改程序计数 器的值。
运行时数据区域按照线程共享分为两大类:
共享:堆和方法区
不共享:栈、本地方法栈、程序计数器
方法区(元空间,jdk7之前是永久代):就是存放类信息,常量信息,常量池信息,包括字符串面量和数字常量等。如果静态变量是对象 ,放的是堆内存中对象的引用,也就是该对象的内存地址,也就是指针。
堆:java虚拟机启动的时候建立Java堆,它是java程序最主要的内存工作区域,几乎所有的对象实例都存放在Java堆中,堆空间是所有线程共享的。主要分为年轻代(包含Eden区,占年轻代内存的8/10和Survivor区,占年轻代内存的2/10,Survivor区中又分为From区,又称S0和To区,又称S1)占整个堆内存的1/3和老年代,占整个堆内存的2/3。
栈:每个虚拟机线程都有一个私有的栈,一个线程的栈在线程创建的时候被创建,一个方法对应一块栈帧内存区域 先进后出。
栈内存中大致分为四大类:
局部变量表: 存放基本数据类型 、对象引用、指令码文件 。
操作数栈 :临时的内存区域,用来存放程序在运行时操作数 。
动态链接:一个指向运行时常量池中该栈帧所属方法的引用,包含这个引用的目的就是为了支持当前方法的代码能够实现动态链。
方法出口:记录各个方法在main方法中的位置,当某一方法挂起后,过段时间继续执行改方法时可以定位到该方法在main方法中的位置。
本地方法栈:就是本地方法运行时的一块区域 ,本地方法是native修饰的 ,是由c语句写的,会打jar包供java语言使用。
程序计数器:是每个线程独有的,记录了当前线程jvm指令码的行数,当前某一线程挂起时,执行完其他线程后,切回当前线程的时候知道从哪里继续执行。
3、堆、栈、方法区的概念和联系
当User类被实例化出来之后会存储到堆内存中一块内存空间中
当我们区使用的时候,都是使用User对象的引用;例:User u1 = new User();
这里u1就是存放在java栈中的,即User真是对象的一个引用。
4、一个方法在虚拟机中的运行过程
进行编译后 得到一个.calss文件,然后我们使用命令>javap -c ***.class生成一个可读的java字节码文件
下面时字节码得文件:
我们可以看到compute方法运行过程:
查看jvm指令手册得知
0:iconst_1 将int类型常量1压入栈
1:istore_1 将int类型值存入局部变量1
2:iconst_2 将int类型常量2压入栈
3:istore_2 将int类型值存入局部变量2
4:iload_1从局部变量1中装载int类型值
5:iload_2从局部变量2中装载int类型值
6:iadd 执行int类型的加法
7:bipush 10 将一个8位带符号整数压入栈
9:imul 执行int类型的乘法
10:istore_3将int类型值存入局部变量3
11:iload_3从局部变量3中装载int类型值
12::ireturn从方法中返回int类型的数据
声明一个局部变量int a=1,先将a分配到操作数栈中,然后在局部变量表中给常量a分配一块空间,并将a移到局部变量列表中a。b变量和a也一样。然后将局部变量1和2取出进行相加,再将整数10压入栈中后出栈,然后进行乘法运行,将运算得结果局部变量c中,将c转为int类型,最后返回。