聊一聊JVM

​ JVM是java虚拟机的缩写,本质上是一个程序,能识别.class字节码文件(.java文件编译后产生的二进制代码),并且能够解析它的指令,最终调用操作系统上的函数,完成我们想要的操作。

​ 关于java语言的跨平台性(一次编译,多次运行),就是应为JVM,可以把它想象出一个抽象层,运行在操作系统之上的,与硬件没有直接的交互,只要这个抽象层JVM正确执行了.class文件,就能运行在各种操作系统之上了。

​ 介绍几个术语:

JDK:java开发工具包,JDK=JRE+javac/java/jar等指令工具

JRE:java运行环境,JRE=JVM+java基本类库

JVM体系结构

​ java虚拟机主要分为五大模块:

类加载器

运行时数据区

执行引擎

本地方法接口

垃圾收集模块

img

​ 方法区是一种特殊的堆,栈里面不回有垃圾,用完就弹出了,否则阻塞了main方法。垃圾几乎都在堆里,所以JVM性能调优%99都针对与堆。

​ 目前最常用的JVM是Sun公司的HotSpot,此外还有BEA公司的JRockit和IBM公司的J9 VM。

类加载器

​ 作用:加载.class字节码文件。

new一个对象的过程 //运行时,JVM将Test的信息放入方法区 public class Test{ public static void main(String[] args){ Student s1 = new Student("Tom");//引用放在栈里,具体的实例放在堆里 Student s2 = new Student("Jerry"); Student s3 = new Student("Victor"); //三个hashCode是不同的,因为是三个不同的对象,对象是具体的 System.out.println(s1.hashCode()); System.out.println(s2.hashCode()); System.out.println(s3.hashCode()); //class1,class2,class3为同一个对象,因为这是类模版,模版是抽象的 Class<? extends Stedent> class1 = s1.getClass(); Class<? extends Stedent> class2 = s2.getClass(); Class<? extends Stedent> class3 = s3.getClass(); System.out.println(class1.hashCode()); System.out.println(class2.hashCode()); System.out.println(class3.hashCode()); } }

首先Class Loader读取字节码文件,加载初始化生成Student模版类。

通过Student模版类new出三个对象。

类加载器的类别 public class Test{ public static void main(String[] args){ Student s = new Student("Tom"); Class<? extends Student> c = s.getClass(); ClassLoader classLoader = c.getClassLoader(); System.out.println(classLoader);//APPClassLoader System.out.println(classLoader.getParent());//PlatformClassLoader System.out.println(classzLader.getParent().getParent());//null,获取不到(C++写的) } }

​ 根据返回结果,级别从高到低有三种加载器:

启动类(根)加载器:BootStrapClassLoader。

c++编写的,加载java核心库,构造拓展类加载器和应用程序加载器

根加载器加载拓展类加载器,并且将拓展类加载器的父加载器设置为根加载器

然后在加载应用程序加载器,应将应用程序的加载器的父加载器设置为拓展类加载器

由于根加载器涉及到虚拟机本地实现的细节,我们无法直接获取到启动类加载器的引用,这就是上面第三个结果为null的原因

加载文件存在于/jdk/jdk1.8/jre/lib/rt.jar

拓展类加载器:PlatformClassLoader

java编写,加载扩展库,开发者可以直接使用标准扩展类加载器

java9之前称为ExtClassLoader

加载文件存在于.../lib/ext

应用程序加载器:AppClassLoader

Java编写,加载程序所在的目录,是java默认的类加载器

用户自定义加载器:CustomeClassLoader

java编写,用户自定义的类加载器,可加载指定路径的class文件

​ 实际上,这些加载器的区别就是加载不同范围或不同路径的.class文件。

双亲委派机制

​ 双亲委派机制是类加载器收到类加载的请求,会将这个请求向上委托给父类加载器去完成,一直向上委托,直到根加载器BootStrapClassLoader。根加载器检查是否能够加载当前类,能加载就结束,使用当前类加载器,否则就抛出异常,通知子加载器进行加载。

​ 举个例子,我们重写java.lang包下的String类:

package java.lang; public class String{ public String toString(){ return "xing"; } public static void main(String[] args){ new String().toString; } } //Error:(1,1) java:程序包已存在于另一个模块中:java:base

​ 我们会发现报错,这就是双亲委派机制起的作用,当类加载器委托到根加载器的时候,String类已经被根加载器加载过一遍了,所以不会再加载,从一定程度上防止了危险代码的植入。

作用总结:

防止重复加载同一个.class,通过不断委托父加载器直到根加载器,如果父加载器加载过了,就不用再加载一遍,保证数据安全。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wspppz.html