惠州建设银行网站,梧州seo排名,网站开发课程设计报告,福州最新通告今天目录
一、JVM内存区域划分
①程序计数器(每个线程都有一个)
②栈#xff1a;保存了局部变量和方法调用的信息(每一个线程都有一个栈) 如果不停地调用方法却没有返回值#xff0c;会产生什么结果
③堆(每一个进程都有一个堆#xff0c;线程共享一个堆) 如何区分一个变量是…目录
一、JVM内存区域划分
①程序计数器(每个线程都有一个)
②栈保存了局部变量和方法调用的信息(每一个线程都有一个栈) 如果不停地调用方法却没有返回值会产生什么结果
③堆(每一个进程都有一个堆线程共享一个堆) 如何区分一个变量是处于栈上还是堆上呢 ④方法区存放的是类对象)
类对象是什么
二、类加载机制
步骤1Loading
步骤2Linking验证、准备、解析
①验证验证Class文件是否符合规范
②准备给静态变量分配内存 ③解析初始化类的常量池当中的一些常量
步骤3初始化(初始化对象为静态属性赋值)
双亲委派机制
双亲委派模型
JVM类加载器是什么(3个类加载器)
标准库的String类是怎样被加载的
自定义的Test类是怎加加载的
双亲委派模型的好处 类加载一定要双亲委派模型吗 一、JVM内存区域划分
JVM的内存区域被划分为了以下4个部分 程序计数器、栈、堆、方法区。 下面将分别介绍这几个区是干什么的。 ①程序计数器(每个线程都有一个) 这一个区域是内存当中最小的区域。保存了下一条要执行的指令的地址在哪里。 指令就是字节码。程序要运行JVM就得把字节码加载起来存放到内存当中。 当程序把一条条指令从内存当中取出来放到CPU上面执行的时候也需要随时记住执行到哪一条了因为CPU是并发执行命令的不是只给一个进程提供服务的 每一个线程都会有一个程序计数器。因为操作系统是以线程为单位进行调度的每一个线程都需要记录自己执行的位置。 ②栈保存了局部变量和方法调用的信息(每一个线程都有一个栈) 当每调用一个新的方法的时候都会涉及到入栈操作。每执行完一个方法的时候就会把这一个方法从内存栈当中移除。 当A方法内部调用B方法然后在B方法内调用C方法的结果是怎样的呢 首先会在内存栈当中存放A方法的有关信息。然后调用B方法的时候在栈中存放B方法的信息。最后调用C方法的时候会在栈当中存放C方法有关的信息。 此处有关的信息有方法的局部变量、方法传入的参数形参、调用的位置、返回的位置等等信息。 当方法执行完毕或者return之后方法对应的信息也会随之从内存栈上面消失。每一个存放方法的区域被称为一个栈帧。 如果不停地调用方法却没有返回值会产生什么结果 运行一下程序可以发现 此处抛出了一个错误信息StackOverflowError JVM的栈空间是比较小的但是也就一般几M或者几十M因此在上述的调用过程当中栈很有可能会满了的。 对于栈来说每一个线程都有一个栈。不同的线程有不同的栈 ③堆(每一个进程都有一个堆线程共享一个堆) 堆是内存内存空间当中最大的区域。new出来的对象就是在堆当中的。那么也就意味着对象的成员变量也是存储在堆当中的。 如何区分一个变量是处于栈上还是堆上呢 局部变量(也就是方法内部创建的基本数据类型变量)都存储在调用这个方法的线程的栈上。 成员变量和new出来的对象都存放在堆上面。但是方法内部对于对象的引用是保存在栈上面的。 ④方法区存放的是类对象)
方法区当中存放的是一个类的.class对象(二进制字节码)。
这里的这个class对象就是保存在方法区当中的。
类对象是什么 类对象描述的就是它对应类当中的属性、方法、以及各自的权限描述符。 此外一个类当中的static方法、static属性也是属于类对象的。这些方法、属性又被称为类方法、类属性。 二、类加载机制
简单来说就是把.class文件加载到内存当中构建类对象。
类加载分为3个步骤 步骤1Loading 步骤2Linking 步骤3Initialization 下面详细说明一下每一个步骤是干什么的 步骤1Loading 先找到对应的.class文件然后打开并且读取.class文件。同时初步生成一个类对象(但是不是真正使用的对象)。 1)通过一个类的全限定名来获取此类的二进制字节流 2)将这个字节流所代表的静态存储结构转化为方法运行时候的数据结构 3)在内存当中生成一个代表此类的java.lang.Class对象。然后把这个Class对象放入到方法区当中作为方法区这个类的各种数据的访问入口。 下面来看一下这个二进制的.class文件究竟包含了什么。
下图就是一个ClassFile的图示。
其中左边的u4代表的就是u2等信息代表的是占了多少个字节。
u4就是4个字节的unsigned int。u2就是2个字节的unsigned int。 步骤2Linking验证、准备、解析
由上图也可以看到在连接部分分为了三个步骤验证、准备、解析
首先看一下验证这个部分是做什么的 ①验证验证Class文件是否符合规范 确保Class文件的字节流中包含的信息符合《Java虚拟机规范》的全部约束要求。如果想读取Class文件的内容就需要先验证一下是否符合规范。验证的内容有 文件格式验证 字节码验证 符号引用验证。 ②准备给静态变量分配内存
例如给static修饰的变量分配内存并且设置上初始的值也就是默认值。
例如下面的代码当中有一个属性为id它被static修饰并且它的值为123。
但是在现在这个阶段它真实的值还是0。
class Member{private static int id123;private String name;
} ③解析初始化类的常量池当中的一些常量 在前面我们也提到了.class文件当中包含了一些常量每一个常量都有一个编号。那么这个时候就是初始化一些常量的时候了。 步骤3初始化(初始化对象为静态属性赋值) 此时就是针对对象进行初始化的操作在这一步的基础上产生对象。 同时在这一个步骤上会把静态的变量给赋值上它对应的值。 例如在前面的时候提到了在准备阶段为一个静态的变量赋上默认的值。但是并没有为它赋值上真正的值。那么就是在初始化的阶段为它赋值上真正的值。 双亲委派机制
首先先来体验一下双亲委派机制。
让一个类B继承自A。然后在A这个类当中包含以下几个内容 A的构造方法、一个构造代码块、一个静态代码块。 然后让B继承自A在B这个类的内部包含以下的几个内容 B的构造方法、B的构造代码块、B的静态代码块。 最后令一个Test类继承自B类并且在Test类当中包含一个mian方法。main方法当中连续两次调用new Test()。
class A {public A() {System.out.println(A的构造方法);}{System.out.println(A的构造代码块);}static {System.out.println(A的静态代码块);}
}class B extends A {public B() {System.out.println(B的构造方法);}{System.out.println(B的构造代码块);}static {System.out.println(B的静态代码块);}}/*** author 25043*/
public class Test1 extends B {public static void main(String[] args) {System.out.println(第一次new);new Test1();System.out.println(第二次new);new Test1();}
}运行之后结果是
根据以上的特点可以得出来双亲委派机制加载类实例的几个原则 1、类加载首先需要加载静态的代码块先父类静态代码块然后子类静态代码块 2、静态代码块只会在类加载的时候执行1次。若重复加载(重复new对象)那么只会执行一次。 3、构造代码块和构造方法每一次new都会执行。并且构造代码块一定优先于构造方法执行。 4、无论是静态还是实例代码块一定都是父类在子类之前。 总结一下那就是
静态优先且唯一、父类优先、代码块优先。 为什么在输出第一次new之前先输出了第一次加载A和第一次加载B呢 因为如果想要执行main方法首先需要加载Test类。但是由于Test继承于B类然后B类又继承于A类。因此会首先加载顶级父类A的静态代码块然后再加载下一级父类的静态代码块。 双亲委派模型
在上面的文章当中我们提到了类的加载分为3个阶段 第一阶段Loading阶段 第二阶段Linking阶段 第三阶段Initialing阶段。 Loading阶段主要负责的就是加载一个类的字节码文件并生成一个Class对象。 而双亲委派模型描述的是JVM当中的类加载器如何根据全限定名类名包名例如Java.lang.String)找到.class文件的过程这个过程属于Loading阶段当中比较靠前的阶段。 JVM类加载器是什么(3个类加载器)
JVM的类加载器主要是以下的3个 1、BootStarpClassLoader负责加载标准库当中的类例如String、List等等 2、ExtensionClassLoader负责加载JDK当中的扩展类 3、ApplicationClassLoader负责加载当前目录当中的类。 每一个类加载器负责加载自己对应的目录。 而上述的双亲委派模型就描述了找目录的过程上述3个类加载器是怎样进行配合的。 下面举一个例子
标准库的String类是怎样被加载的 第一步程序启动先进入ApplicationClassLoader类加载器。 第二步然后在ApplicationClassLoader当中检查一下它的父加载器(ExtensionClassLoader)是否已经加载过了。如果没有加载过那么就调用ExtensionClassLoader来进行加载。 第三步ExtensionClassLoader也会检查一下它的父加载器(BootStarpClassLoader)是否加载过。如果没有那么就调用最高的父加载器(BootStarpClassLoader)来进行加载。 然后查找标准库的目录Java.lang.String并且完成Java.lang.String的加载。 自定义的Test类是怎加加载的 自定义的Test类也会经过上述 由ApplicationClassLoaderExtensionClassLoaderBootStrapClassLoader的三个加载过程。 但是由于BootStrapClassLoader负责的目录是标准库的目录那么肯定找不到Test类于是回到下一级的目录ExtensionClassLoader进行加载。同样也找不到Test类。最后回到ApplicationClassLoader负责的目录也就是当前项目的目录进行加载最终找到了Test类进行加载。 如果在最后的阶段也没有找到Test类那么就会抛出一个异常ClassNotFoundException 双亲委派模型的好处 当用户自定义的类如果和派生类/标准库当中的类如果全限定名类名称包名称重复了仍然可以准确地加载标准库当中的类,而不是加载用户自定义的类 在上述过程当中如果查找到标准库当中有Java.lang.String这个类就不会再回去加载了。 此处我自定义一个类java.lang.String) 但是如果在其他的地方进行new发现new的是标准库当中的类。 类加载一定要双亲委派模型吗 不一定双亲委派模型只是JVM内部实现的一个类加载机制。 例如Tomcat加载webapps当中的类就没有使用双亲委派模型因为Tomcat当中的类都是已经被开发好了的无需多一道从标准库回去查询的工序。 双亲委派模型只是避免用户自定义的类和标准库当中的类的全限定名重名时候加载了用户的类。但是如果已经确定了用户自定义的类的全限定名和标准库的不一致那么就没有必要多去检验一次父加载器当中是否有加载了。