JVM虚拟机
概念:
Java Virtual Machine,JVM 是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的
JVM 是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的
为什么要使用JVM?
Java可以说是环境最好的语言,一次编译到处运行,这一切都基于jvm虚拟机来保证,实际上所谓的一次编译到处运行也并非是这样,是因为有不同版本的JVM对应不同的操作系统,保证了java语言的一次编译到处运行的这一特性
JVM的主要作用在于:软件层面的机器码翻译和 内存管理
JVM的内部结构
-
类加载子系统:
-----引导类加载器(Bootstrap ClassLoader),是由C++实现的,它是JVM的根ClassLoader,但是不继承于java.lang.ClassLoader,
作用:JVM启动时初始化此ClassLoader,并由此ClassLoader完成$JAVA_HOME中jre/lib/rt.jar(Sun JDK的实现)中所有class文件的加载,这个jar中包含了java规范定义的所有接口以及实现。
----- Extension ClassLoader 拓展类加载器
作用: JVM用此classloader来加载扩展功能的一些jar包
-----System ClassLoader或者AppClassLoader
作用: JVM用此classloader来加载启动参数中指定的Classpath中的jar包以及目录,在Sun JDK中ClassLoader对应的类名为AppClassLoader。
-----自定义加载器(User-Defined ClassLoader)是程序员通过继承java.lang.ClassLoader类的方式实现自己的类加载器,以满足一些特殊的需求而不需要完全了解Java虚拟机的类加载的细节,Java虚拟机规范将所有派生于抽象类ClasssLoad的类加载器都划分为自定义类加载器
作用:
1)运行时装载或者卸载类,常用于实现脚本语言、用于bean的生成器、允许用户定义的拓展性、允许命名空间之间的通信(CORBA/RMI协议的基础)。
2)改变java字节码的装载(例如用于Java字节码的加密装入)
3)修改已装入的字节码
注意:
1)所有的类加载器都继承与ClassLoader类。并且BootstrapClassLoader、ExtClassLoader、AppClassLoader继承于Java.net.URLClassLoader,可以从本地和网上下载字节码。URLClassLoader继承ClassLoader。;
2)BootstrapClassLoader是最先加载的,它然后依次加载出ExtClassLoader、AppClassLoader对象。
-
运行时数据区
1)程序计数器: 指向当前线程正在执行的字节码指令的地址,也就是行号
-如果执行的是一个Java方法,计数器记录的是正在执行的虚拟机字节码指令的地址
-如果执行的是一个Native方法(native修饰的方法实际上是由c语言编辑实现的,可以由java调用,简单地讲,Native Method就是由一个java 调用非java代码的接口),计数器的值则为空特性:内存空间小,线程私有
作用:字节码解释器就是根据改变这个计数器来选取下一个需要解释的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能均依赖技术其完成
理解:程序最小的执行单位是线程,而线程在CPU上执行的时候是抢占式的,当正在执行的线程被挂起时,该线程在下次要被执行,就需要一个记录该线程之前运行到的位置(就像一本书A读到一半,跑去读另一本书B,当你在次读A书时,就需要“书签”这样一个事物来做标记,这里可以参照HDFS的操作模式 NameNode元数据这一块来理解)2)虚拟机栈:
定义:存储当前线程运行方法时所需要的数据、指令和返回地址,描述的是 Java 方法执行的内存模型:每个方法在执行时都会床创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。特性:线程私有,生命周期与线程一致
局部变量表:存放了编译期可知的各种基本类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference 类型)和 returnAddress 类型(指向了一条字节码指令的地址)
注意:局部变量表所需的内存空间在编译期间就会车队分配完成,因为在进入一个方法时,这个方法需要在栈帧中分配多大的局部空间是完全确定的,方法运行期间局部变量大小是不会改变的。
3)本地方法栈
和虚拟机栈类似,只不过他存储的是当前线程调用的本地方法所需要的数据、指令和返回地址等,本地方法栈则为虚拟机使用到的 Native 方法 服务。,有些虚拟机直接就将本地方法栈和虚拟机栈合二为一了4)方法区
这块区域属于线程共享群区,主要存储的信息包含已经被虚拟机加载了的类信息(类的元数据)、常量、静态变量、JIT(编译器编译后的代码)等数据运行时常量池(原隶属于方法区),存放的是编译期生成的各种字面量和符号引用,运行时常量池有一个重要的特征—动态性,即运行期间依然可以将新的常量放入池中(参考热加载),我们常用的有String类的intem()方法,JDK8以前在方法区中,JDK8以后转移到了堆内
5)堆(Heap)
对于绝大多数应用来说,这块区域是 JVM 所管理的内存中最大的一块,属于线程共享区域,在虚拟机启动时创建,他唯一的作用就是存放对象实例和数组,内部会划分出多个线程私有的分配缓冲区(Thread Local Allocation Buffer, TLAB)。可以位于物理上不连续的空间,但是逻辑上要连续。
根据虚拟机规范的描述是:所有的对象实例及数组都要在堆上分配。当然因为jvm的版本和种类不同,这一点并不绝对 -
执行引擎(一般都是JIT编译器和解释器共存)
JIT编译器(主要影响性能):编译执行;一般热点数据会进行二次编译,将字节码指令变成机器指令,将机器指令放在方法区缓存。
字节码解释器(负责响应时间):逐行解释字节码
该区域也是垃圾收集器管理的主要区域,现今垃圾回收器基本上采用分带收集算法。
JDK1.8之前的内存模型划分如下:
JDK1.8之后内存模型划分:
JDK1.8之后有了Meta Space(自动扩容),其设计目的在于规避永久代内存溢出的问题,常量池也从方法区移到了堆中
大致先到这里,具体的一些细节性的概念且等待下次更新,如果文章中有问题或者图解哪里有不对的,希望各位批评指正,以求共同进步,谢谢!!!!!