JVM

JVM

一、JVM的运行机制概要

如果要运行Java程序,首先要编写Java的源文件(*.java),之后Java的 编译器 将源文件编译成字节码文件( *.class),之后再通过 类加载器 将字节码文件加载到jvm中,其中 与程序运行在密切 的是jvm中的 运行时数据区(Runtime data area)
JVM的运行概要

二、运行时数据区(JVM内存模型)

程序的运行最为重要的一块区域就是运行时数据区,其中运行时数据区可以分为如下几个子区域:
运行时的数据区

程序计数器:

程序计数器是用于存放下一条指令所在的单元的地址的地方
如果线程正在执行的是一个Java方法,这个计数器记录的是正在执行虚拟机字节码指令的地址;

虚拟机栈:

该区域是线程私有的,虚拟机栈的生命周期和线程相同,在 执行一个Java方法 的时候会在该区域创建一个栈帧, 栈帧用来保存局部变量表(在方法中定义的变量或者方法的参数都是方法的局部变量、动态链接、方法出口,操作数栈等等信息) ,其实每个 方法从执行到执行完毕就是一个入栈与出栈的过程
当入栈的速度大于出栈的速度的时候回出现栈内存溢出(Exception in thread"main"ava.lang.StavkOverflowError)。

本地方法栈:

本地方法栈和虚拟机栈的作用很相似,不同点事虚拟栈是为Java方法服务的,本地方法栈是为 native 方法服务的(这种本地方法是没有方法体的,可以算是一个抽象方法,具体的实现是本地操作系统的c语言写的一些函数库中的方法实现的)

堆:

堆是运行时数据区中最大的一块子区域, 创建的实例对象和数组都需要在堆内存开辟空间,堆空间是线程共享的区域,该区域也是 gc (垃圾回收)重要处理的一个区,所有堆又被称为“GC堆”。

方法区:

这是对永久代的实现,永久代是在JDK1.8之前出现的概念,在JDK1.8开始就没有了永久代,而是使用了元空间代替了。
方法区也是一块线程共享的区域,该区域主要保存的是已经加载到jvm中的类的信息、常量、静态方法等等

三、Java类的加载机制

类加载指的是将类的.class文件的二进制数据读入到内存中
将类信息保存在方法区内(在jdk1.8之前,在jdk1.8开始就用元空间代替)
②然后在堆区创建一个java.lang.Class 对象,用来封装类在案发区类的数据结构,可以使用该对象操作方法区中类的所有信息
。类的加载的最终产品是位于堆区中的Class对象,Class对象封装了类在方法区内的数据结构,并且提供了访问方法区内的数据结构的接口(简单理解为方法)
a.加载会将类的一些数据的结构信息保存到方法区
b.之后会在堆区创建一个对象用于操作获取方法区中对应的类的数据以及信息等,每个类都会在加载的时候创建java.lang.Class类的对象,此时每个类的对应的堆区的这个对象的类型都是Class类型怎么区分呢?这个类是一个泛型类,使用泛型类区分。

堆区对应的这个Class类对象提供了许多方法用于操作方法区中类的数据结构。
比如说要加载一个Car类:
1.将类的结构信息加载到方法区中
2.在堆区创建一个数据Car类的Class对象,作用是提供方法让我们可以操作方法区中 Car数据结构信息

以上说了可以使用类的Class对象操作类的构造信息,你要使用对象应该先取得对象,这个Class对象怎么取得呢? 有三种方式可以取得:
第一种方式:使用对象的 getClass()方法,类可以有自己的普通对象 (new出来的),可以使用这个对象的 getClass()方法,这个方法是继承自最大父类Object
第二种方式:使用Class类的一个静态方法:forName(“类的名”)
第三种方式:可以直接使用“类名.class”直接取得。
这三种方式最终取得的是在加载的时候保存到堆区对应的Class类对象。取得之后就可以调用Class类对象的方法操作保存在方法区中的类的信息了。
Demo:操作方法区中的类的构造方法

public class Test  {
    public static void main(String[] args) throws  Exception {
       Class<?>  classObj =  Link.class;
       classObj.newInstance(); //实例化保存在方法区中的Link类的对象(本质是调用了Link类的构造方法)
    }
}

类加载的装载阶段
以上我们说了要将类的字节码文件(*.class)转换成二进制形式加载到内存中,这个加载的过程是 类加载器(ClassLoader),
类加载器分为引导类加载器、扩展类加载器、应用程序加载器、自定义加载器。

四、1、类加载器

1、类加载器的作用是将.class文件加载到内存中的组件*

类加载器的分类

引导类加载器(bootstrap class loader)

Bootstrp 加载器(启动类加载器或者叫做引导类加载器)是用C++语言写的,它是在Java虚拟机启动后初始化的,它主要负责加载%JAVA_HOME%/jre/lib

扩展类加载器(extensions class loader)

启动类加载在启动的时候会将ExtClassLoader加载进来,并且将ExtClassLoader的父加载器设置为Bootstrploader(不是使用extends的概念实现父子关系),ExtClassLoader是用Java写的,具体来说就是sun.mise.Launcher$ExtClassLoader , ExtClassLoader主要加载%JAVA_HOME%/jre/lib/ext。

应用程序加载器(application class loader)

应用程序加载器又叫做系统类加载器(Bootstroloader)加载完扩展类加载器(ExtClassLoader)后,就会加载AppClassLoader,并且将 AppClassLoader 的父加载器指定为 ExtClassLoader。AppClassLoader也是用Java写成的,它的实现类是sun.misc.Launcher$AppClassLoader,另外我们知道ClassLoader中有个getSystemClassLoader方法,此方法返回的正是AppclassLoader.AppClassLoader,系统类加载器主要负责加载classpath所指定的位置的类或者是jar文档,它也是Java程序默认的类加载器。
可以通过继承java.lang.ClassLoader的方式实现一个自定义的加载器,以满足一些特殊的需求。

2、java.class.ClassLoader的作用

ClassLoader主要对类的请求提供服务,当JVM需要某类时,由ClassLoader返回这个类的class对象。 ClassLoader负责载入系统的所有Resources(Class,文件,来自网络的字节流等),通过ClassLoader从而将资源载入JVM

Deme:观察

 package com.sun;
public class Test  {
    public static void main(String[] args) throws  Exception {
        //取得系统类加载器
        System.out.println("系统类加载器:"+ClassLoader.getSystemClassLoader());
        //取得系统类加载器的父加载器
        System.out.println("系统类加载器的父加载器:"+ClassLoader.getSystemClassLoader().getParent());
        //取得扩展类加载器的加载器
        System.out.println("系统类加载器的父加载器:"+ClassLoader.getSystemClassLoader().getParent().getParent());
    }
}

实现

2、双亲委派加载机制

1.当AppClassLoader加载一个class时,先到自己的缓存中查找,如果缓存中不存在它先不会自己去加载这个类,而是把类加载请求委派给父类加载器ExtClassLoader去完成。

2,当ExtClassLoader加载一个class时,先到自己的缓存中查找,如果缓存中不存在它先不会去尝试加载这个类,而是把类加载请求委派给BootStrapClassLoader去完成。

3.当BootStrapClassLoader加载一个class时,先到自己的缓存中查找,如果缓存中不存在它先会去尝试加载这个类,如果失败,则会使用ExpClassLoader去尝试加载。

4,若ExtClassLoader也加载失败,则会使用AppClassLoader来加载,如果AppClassLoader也加载失败,则会报出异常ClassNotFunExCEPTION。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值