JVM的由来
①、类比计算机:
A、把jvm可以理解为一个计算机就可以了,为什么会称之为虚拟机呢?我们用到的电脑就是计算机,是实实在在的机器,而实的对立是虚,所以就有计算机,由于这个名字不好听,所以起名为虚拟机。
作用:平时的计算机是运行软件的,所以java虚拟机也应该是运行软件的
区别:实实在在的计算机是操作系统,什么软件都可以运行,而jvm只能够运行class文件软件系统的机器。通过jvm代替计算机的功能。
上面是通过计算机来类比的,可能无法了解内部的组件。
B、 jvm就是对实实在在的计算机上封装了而已,模拟计算机的功能,虚拟的东西,可以把jvm想象为软件而已。
C、上面的jvm是和计算机绑定的,当计算机系统升级的时候,这个jvm也应该升级,必须要适配更新的操作系统。如果jvm没有封装计算机的话,根本是垮不了平台。jvm是依赖操作系统实现的。
D、写的程序是存储在计算机的寄存器里面的。
②、类比后台的架构:
以业务为依据:后台的依据是前台的业务对象class文件。可以把数据库类比为内存。把后台架构类比为jvm,jvm也是有结构的。把前台的模型转换成后台的模型,最后把数据存储到内存,然后从内存中取出数据来使用。
jvm内存模型:
这是jvm整体的运行流程图,而在jvm虚拟机中主要的是三大块,java类加载器,运行时数据区,还有执行引擎。下面主要写运行时数据区的5个核心的组件。这5个核心组件在jvm调优的是时候很重要:
①、jvm的内存模型/结构
元数据空间,java堆,Java栈,本地方法栈,程序计数器
提出一个疑问:为什么叫做内存模型呢?
解释:因为不但把它们加载出来,还要存储,把这些数据存储到数据库,也就是存储到内存里面去,这些组件要存储到内存。所以把它叫做内存模型,可以把这5个的组件当成5张表来类比。
一、元数据空间:
①、在jdk1.8之前是用方法区的(官方名字是永久代),而之后是用元数据空间代替的
元数据空间是一个抽象的概念,可以把它当成一张表,可以把元数据空间当作表。而表头就是存储的一些信息,除了表头还有数据,元数据空间里的数据,就是java定义的所有的类文件都放到内存。元数据空间就是发的java类,类文件的5种信息都需要存放:类名,父类,接口,属性(基本类型,复杂数据类型),方法,这5个是java类的标配。
②、我们写的类称之为资源,Java中用元数据描述资源,可以用注解@MXBean来描述一个元数据,这个java类变为class文件的一个元素:
public class App extends App2 implements Demo
{
// 定义常量
private final String name="2";
// 定义静态变量
private static String name11;
// 属性
private App3 app3;
private String name1;
private String name2;
private static App3 app5 = new App3();
public App(String name1){
this.name1 = name1;
this.name2= name2;
}
// 方法
public String demo(String name){
String name22 = "1";
String password = "2";
String password1 = "3"; // 保留记录行号
name22 = name22 + password;
app3 = new App3();
app3.demo();
synchronized (app3) {
}
app3.demo();// 方法的符号引用
System.out.println("222");
System.out.println("333");
return "";
// 对象如何调用?
// 对象该如何回收呢?
// 方法怎么执行?
}
public String demo1(){
System.out.println("222");
System.out.println("333");
return "";
}
public static void main( String[] args )
{
System.out.println( "Hello World!" );
for(int i = 0 ; i < 10000; i ++){
byte[] bs = new byte[1024 *1024 *10];
}
}
}
③、在元数据空间里面存储的是类的所有的信息:元数据是描述数据的数据。
* 1、当前类名信息
* 2、字段信息
* 3、方法信息
* 4、父类信息
* 5、接口信息
* 6、引用信息(符号引用=====内存地址引用):类比spring中的Bean,先换成字符串,然后加载到内存。
* 6.1 类引引用信息(符号引用【一个字符串【类的权限定名】】=====内存地址引用)
解释下符号引用:下面的是一个复杂的数据类型,而这个复杂的数据类型需要导入一个包,这是自定义的App3,可是在App这个类里面不认识这个程序,还没加载到内存中就把class文件看成是一个字符串。我们当引入包的时候可以看成是一个引用。
privateApp3 app3;
* 6.2 方法引用信息(符号引用【一个字符串【方法描述符】】=====内存地址引用)
下面的图中的代码就可以称之为方法的引用,app3.demo():同样这个App的class文件不认识,第一次也会把它抽象成字符串。然后再加载到内存,分配相应的地址:
* 6.3 字段引用信息(符号引用【一个字符串【字段描述符】】=====内存地址引用)
这里只是做一个字符串的引用,而没有分配内存地址。
上面的过程可以类比 :数据库中的表来存储数据:存储数据的时候会返回一个id,地址就相当于是一个Id。可以理解一个表的增删改查。存储到内存的时候也是返回一个地址。数据库返回的是id,来标识这个数据。
* 7、常量信息:也是存储到元数据空间里面
// 定义常量
private final String name="2";
* 8、 静态变量信息:静态变量属于类对象的,类对象里的数据也会存储到元数据空间里面。
// 定义静态变量
privatestaticString name11;
注意:像下面的基本变量就不是存储到元数据空间里面的。字段多的话,会把字段来做一个抽象,方法也需要进行抽象,因为方法不知道有多少个,抽象出相同的规范。
* 9、classLoader引用
class对象是由类加载器创建和加载的,类加载器也是对象,所以在元数据空间里面也有 classLoader的引用。
* 10、class 对象的实例
整个类会转换成class文件,在反射中有一个class对象:getClass(),所以这个时候也会存储一个class对象的实例。
解释下永久代:不能变的东西就称之为永久代,存的数据是永久代,基本上是不会变的数据,类被加载成class文件是不可以修改的,更好听的名字就是元数据空间。
二、java堆:java堆也可以看成是一张表:
①、上面的是通过new关键字来创建的对象,创建对象的依据定义的的类来创建,因为元数据可以称之为所有数据的母数据,是所有子数据的母数据。都是由原数据扩展和延申的。
所以,第一步:将元数据所处的类的资源加载进来,
第二步:然后再使用这个元数据进行set和get过程。
从元数据里面获取类引用信息,然后再进行创建,把App3动态的加载到java虚拟机之后
来进行创建对象
类名找到就可以获取到class文件,class文件加载到java虚拟机
②、app3=new App3():依赖的是符号引用,这个App3还没有加载进来。app3=new App3():会把这个元数据加载到java虚拟机,创建App3的实例,动态加载,把App3对象实例数据存到java堆里的,对象的值都放到堆里面的。属性的名存储在方法区,非类属性名在堆里的。
③、Java堆可以看成一张表,一张表中的一行就称之为一个对象
栈只是存储的是方法代码,存储方法里面的引用信息。
数据的引用在栈,指向堆,引用的地址信息存储在堆,值是存储在堆里的。
属性的符号引用是可以存储在方法区。
④、我们的对象还可以被锁住,锁的标识也会存储到堆中
类似hash,线程都是存储在堆里面的
以前是堆和元数据空间是在一起的,现在是分开的。
看下元数据空间和java堆,栈。
可以把元数据空间比喻成仓库,从仓库里面各有所需放到自己的空间里面去。执行数据的话是要依赖对象的数据,然后最后通过执行引擎来执行的。
元数据空间是存储所有的数据的。把值取出来放入到堆中。
三、java栈:
存可执行的字节码,就是方法内部的一些可执行的指令。把方法从元数据取出放到栈 里面的。方法存在java栈中,每个方法抽象出栈帧。下面栈中存储的信息:
* 1、本地变量表====(参数,局部变量)
* 2、操作数栈=====(参数值,局部变量值)
有了变量之后还有值,操作数栈就是压入值和出入值的情况,操作数栈由值和指令组合。操作数栈里面存的都是值,由指令将这些值压入到操作数栈里面去。
* 3、动态链接=====(调用方法的符号引用,进行转换成内存地址)
* 4、返回地址=====(返回值)
* 5、其它信息
java栈的执行原理
上面勾起来的代码可以用下面的图解释:String name22="1";String password="2"3,String password1="3",是set的过程,就是压栈的过程,name22=name22+password:是get的过程,出栈。
四、本地方法栈:
java栈是一模一样的(就是调用c语言方法的 native,调用c语言的接口来实现)
五、程序计数器:
当在方法栈中执行 一段代码的时候:突然之间中断了cpu资源被另外一个线程给打断了
如何去保留这行的记录号呢?保留程序记录呢?
程序计数器:记录代码的执行行号:保留现场状态
当获取到cpu资源的时候会继续往下执行
GC:基本介绍(明天会详细写)
Gc(删除功能)===>类比:删除数据库的一张表的数据而已
* 1、什么对象需要收集
* 就是收集内存里面没有引用的对象定位
* 2、怎么去定位到对象没有被引用?
* 2.1 引用计数(存储在堆对象数据里面(对象头))
* 一个对象被引用一次,那么应用计数就加1,如果说没有引用,减1 引用计数等于0,就表示这个对象需要回收
* 3、在什么时机下面Gc回收呢?
* 3.1 新对象在创建的时候,内存空间不足的时候,开始Gc回收(删除对象实例),
* 4、特点(会中断所有的线程执行,降低程序响应速度)
由于算法不同,所以中断线程的时机不一样
内存组件包括:类加载器,运行时数据区(内存模型,执行引擎)
jvm的优化的地方就是优化运行时数据区的5个核心组件还有gc回收器