jvm指的是Java虚拟机,一种能够运行Java字节码的虚拟机。它能够模拟具有完整硬件系统功能,运行在一个完全隔离环境中的完整计算机系统。作为一种编程语言的虚拟机,它不只是专注于Java语言,只要生成编译...
jvm指的是Java虚拟机,一种能够运行Java字节码的虚拟机。它能够模拟具有完整硬件系统功能,运行在一个完全隔离环境中的完整计算机系统。作为一种编程语言的虚拟机,它不只是专注于Java语言,只要生成编译文件匹配jvm对加载编译文件格式的要求,任何语言都可以由jvm编译运行。比如scala,kotlin等。
一,JVM的基本组成结构。
jvm由三部分组成:
1)类加载子系统
2)运行时数据区(内存空间,内存结构)
3)执行引擎
结构图如下:
点击添加图片描述(最多60个字)
深入了解运行时数据区:
线程共享:
①方法区(1.8之后已经不叫方法区了,叫元数据区):用来存储被jvm加载的类信息,常量,静态变量等数据。也就是类的所有属性,方法以及构造器,接口代码都在这里定义。
这个区域包含一个很重要的区域叫常量池。class对象除了类的版本,属性,方法等信息外,还有一些常量以及字面值,而这些几乎都存在于常量池里,这部分内容将在被加载的过程中存放到常量池里。
②堆:实例化的对象以及数组都存在这个区域。这个区域的cg最活跃,也就是cg进行垃圾回收的重要场所。这个区域,cg是以分代回收算法进行垃圾回收的。从cg的角度看,堆又可以分为:新生代和老年代。
线程私有:
③程序计数器:也就是一个指针,指向方法区中的方法字节码。也就是用于存储每条线程的执行字节码指令地址。每条线程都会拥有自己的程序计数器,以便线程切换的时候,能使程序正常运行。
④jvm栈:存储着每一个栈帧。每个方法的执行都对应着每一个栈帧,栈帧里放着局部变量表,操作数栈,动态连接,方法出口等信息。每个方法的执行以及结束都对应到栈帧的入栈与出栈。
⑤本地方法栈:这个跟jvm栈差不多,只是jvm栈为我们的java服务的,而本地方法栈是为native 方法服务。
深入了解类加载子系统:
我们的Java文件被jvm编译器编译成class对象之后,在runtime时,由类加载子系统将class字节码文件加载到运行时数据区中。
但是,类加载子系统在类的整个生命周期中主要的工作是什么呢?
类的生命周期:
类的生命周期分为:
加载-->连接-->初始化-->使用-->卸载
而连接过程又分为:验证-->准备-->解析。其实这个过程就是类加载子系统工作的过程。
一,工作过程
1,加载:将class字节码文件加载到运行时数据区中。
2.1,验证:1,检查字节码文件是否正确。2,检查文件头的magic number是否正确。
何为magic number?我们用编辑器打开随意一个class文件,发现都是以CAFEBABE开头。把这数据改掉或者删除,都会出错。这个就是magic number
2.2,准备:给类的静态变量分配内存,赋予初始值。这里的初始值并不是赋值的值。打个比方:private int i = 100; 在准备阶段,并不是将100赋值给i。而是给i赋值0,因为int的默认初始值是0。
2.3)解析:检查指定的类是否引用了其他的类或者接口,是否能正常引入。也就是装载当前类引入依赖的其他类。
3)初始化:这里才是给类的静态变量赋正确的初始值。i的值由0变为100。
使用,卸载这就没什么可说的了。
通过源码了解加载过程:
在rt.jar包中的java.lang.ClassLoader类中,我们可以查看类加载实现过程的代码,具体源码如下:
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
源码的注释写得很清楚了:
1)加载的时候使用了synchronized加锁机制,也就是当前线程有在加载当前这个类时,不允许其他类进行加载。
2)判断这个类是否已经被加载,如果已经加载了,就不再加载。如果没有,则要加载。
我们在看源码时,看到
//从parent这个成员变量中,我们得知
private final ClassLoader parent;
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
}
这是一个很重要的信息~由此引申一个很重要的概念
二,双亲委派机制
我们先了解类加载器的种类:
1,启动类加载器Bootstrap ClassLoader:负责将java_home/lib下的类加载到内存中
2,扩展类加载器Extension ClassLoader:负责将java_home/lib/extde的类加载到内存中
3,系统类加载器Application ClassLoader:负责将classpath的类加载到内存中
4,用户自定义加载器User ClassLoader:负责加载用户自定义路径下的类包
在看源码可以看出,每次加载类的时候,当前加载器都将该类一层一层交给parent,而且每次都检查是否已经加载了,如果加载了就不再加载。当交给parent时,如果parent能加载,则就加载。如果parent都不能加载,则由当前加载器进行加载。
双亲委派的优势:
沙箱安全机制:比如自己写的String.class类不会被加载,这样可以防止核心库被随意篡改
避免类的重复加载:当父ClassLoader已经加载了该类的时候,就不需要子CJlassLoader再加载一次
以上就是jvm的内存结构以及类加载子系统的相关内容。jvm还有许许多多的知识,比如垃圾回收机制,调优等,以后我们慢慢了解
来源:本文内容搜集或转自各大网络平台,并已注明来源、出处,如果转载侵犯您的版权或非授权发布,请联系小编,我们会及时审核处理。
声明:江苏教育黄页对文中观点保持中立,对所包含内容的准确性、可靠性或者完整性不提供任何明示或暗示的保证,不对文章观点负责,仅作分享之用,文章版权及插图属于原作者。
Copyright©2013-2024 JSedu114 All Rights Reserved. 江苏教育信息综合发布查询平台保留所有权利
苏公网安备32010402000125 苏ICP备14051488号-3技术支持:南京博盛蓝睿网络科技有限公司
南京思必达教育科技有限公司版权所有 百度统计