提起JAVA,都知道它有个虚拟机,因此可以跨平台,一次编写,到处运行。但具体原理是咋样的?身为JAVA小白,岂可不知!
一、原理
二、结构
1、类加载器
2、执行引擎
3、运行时数据区域
- 1
- 2
- 3
- 4
- 5
一、原理
JVM实现了JAVA语言最重要的特征:平台无关性。其原理:java程序并不直接在操作系统上执行,而是由JVM执行。JVM屏蔽了与具体平台相关的信息,使得JAVA程序只需编译成适应JVM,可以在JVM上运行的目标代码(.class)就行,JVM再负责解释成具体平台的机器指令执行。同时,JVM还会进行安全检查,是java程序的安全检验引擎。
二、结构
JVM由三大部分组成:
1、类加载器
2、执行引擎
3、运行时数据区域
- 1
- 2
- 3
也有说法是包括本地库接口的。
首先通过编译器把 Java源代码转换成字节码,Class loader(类装载)再把字节码加载到内存
中,将其放在运行时数据区的方法区内,而字节码文件只是 JVM 的一套指令集规范,并不能直
接交给底层操作系统去执行,因此需要特定的命令解析器执行引擎(Execution Engine),将
字节码翻译成底层系统指令,再交由 CPU 去执行,而这个过程中需要调用其他语言的本地库
接口(Native Interface)来实现整个程序的功能。
1、类加载器
具体作用就是将.class
文件加载到jvm虚拟机中去,程序就可以正确运行了。但是,jvm启动的时候,并不会一次性加载所有的.class
文件,而是根据需要去动态加载。
类的加载过程采用双亲委派模型:
1)当前类加载器首先检查自己的加载清单,如果存在该类,则返回之;
2)如果没找到,委托父类加载器去加载;父类加载器重复动作1,递归至祖宗bootstrap ClassLoder(我靠,bootstrap,还以为是前端框架呢。bootstrap,引导之意);
3)都没有,仍然由当前类加载器加载,并纳入自己缓存中。
- 1
- 2
- 3
- 4
- 5
采用双亲委派模型,目的在于更好的保证Java平台的安全。所谓的安全,是指java的核心类不被用户自己编写的类所替换。由于核心类在虚拟机初始化时就已加载,而每次加载类前先检查是否存在,所以可以避免核心类被替换。
各种类加载器:
Bootstrap class loader: 启动类加载器(或曰父类加载器)
当运行 java 虚拟机时,这个类加载器被创建,它负责加载虚拟机的核心类库,如 java.lang.* 等。例如 java.lang.Object 就是由根类加载器加载的。需要注意的是,这个类加载器不是用 java 语言写的,而是用 C/C++ 写的。
这个类加载器负责将存放在<JAVA_HOME>\lib目录中的,或者被-Xbootclasspath参数所指定的路径中的,并且是虚拟机识别的(仅按照文件名识别,如rt.jar,名字不符合的类库即使放在lib目录中也不会被加载)类库加载到虚拟机内存中。
Extension class loader:扩展类加载器
这个加载器加载出了基本 API 之外的一些拓展类。
这个加载器由sun.misc.Launcher$ExtClassLoader实现,它负责加载<JAVA_HOME>\lib\ext目录中的,或者被java.ext.dirs系统变量所指定的路径中的所有类库,开发者可以直接使用扩展类加载器。
AppClass Loader(SystemAppClass):
加载应用程序和程序员自定义的类。
2、执行引擎
作用: 执行java编译好的字节码,或者执行本地方法
3、运行时数据区域
JVM 运行时数据区域 (JVM Runtime Area) 其实就是指 JVM 在运行期间,其对JVM内存空间的划分和分配。JVM在运行时将数据划分为6个区域来存储:
Program Counter (PC) Register:程序计数器
Java Virtual Machine Stacks:Java虚拟机栈
Heap Memory:堆内存
Method Area:方法区
Run-time Constant Pool:运行时常量池
Native Method Stacks:本地方法栈
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
我们写的所有程序都被加载到这里,按不同类别存放在
堆内存
Java虚拟机栈
本地方法栈
程序计数器
方法区
- 1
- 2
- 3
- 4
- 5
1)从线程的角度理解Java Runtime Area
a.线程私有:
程序计数器 Java虚拟机栈 本地方法栈
,生命周期与线程一致;
这3个内存区域是不需要进行垃圾回收的。因为他们的生命周期是和线程同步的,随着线程的销毁,他们占用的内存会自动释放。
b.线程共享:
堆内存,方法区,运行时常量池
,生命周期与JVM一致。
方法区
和堆内存
需要进行垃圾回收,回收的对象就是那些不存在任何引用的对象。
2)从存储内容理解Java Runtime Area
方法区
和常量池
存储类的信息
堆内存
存储对象信息
程序计数器
,Java虚拟机栈
,本地方法栈
存储线程的信息
3)堆内存、方法区和运行时常量
方法区的作用是存储 Java 类的结构信息,当我们创建对象实例后,对象的类型信息存储在方法区之中,实例数据存放在堆内存中;
运行时常量池是方法区的一部分,所以也是全局共享的。其作用是存储 Java 类文件常量池中的符号信息。
运行时常量池就是将编译后的类信息放入方法区中,也就是说它是方法区的一部分。 运行时常量池用来动态获取类信息,包括:class文件元信息描述、编译后的代码数据、引用类型数据、类文件常量池等。 运行时常量池是在类加载完成之后,将每个class常量池中的符号引用值转存到运行时常量池中。
4)直接内存(Direct Memory)
直接内存并不是JVM管理的内存,而是JVM以外的机器内存。比如,你有4G的内存,JVM占用了1G,则其余的3G就是直接内存。
JDK中有一种基于通道(Channel)和缓冲区(Buffer)的内存分配方式,将由C语言实现的native函数库分配在直接内存中,用存储在JVM堆中的DirectByteBuffer来引用。由于直接内存收到本机器内存的限制,所以也可能出现OutOfMemoryError的异常。
=====================================
2022.09.18
【什么是永久代?它和方法区有什么关系呢?】
如果在HotSpot虚拟机上开发、部署,很多程序员都把方法区称作永久代。可以说方法区是规
范,永久代是Hotspot针对该规范进行的实现。在Java7及以前的版本,方法区都是永久代实现
的。
【什么是元空间?它和方法区有什么关系呢?】
对于Java8,HotSpots取消了永久代,取而代之的是元空间(Metaspace)。换句话说,就是方
法区还是在的,只是实现变了,从永久代变为元空间了。
【HotSpot】
SUN的JDK版本从1.3.1开始运用HotSpot虚拟机, 2006年底开源,主要使用C++实现,JNI接口部分用C实现。
HotSpot是较新的Java虚拟机,用来代替JIT(Just in Time),可以大大提高Java运行的性能。
Java原先是把源代码编译为字节码在虚拟机执行,这样执行速度较慢。而HotSpot将常用的部分代码编译为本地(原生,native)代码,这样显着提高了性能。 HotSpot JVM 参数可以分为规则参数(standard options)和非规则参数(non-standard options)。