问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501
你好,欢迎来到懂视!登录注册
当前位置: 首页 - 正文

jvm会先去方法区中找有没有相应类的.class存在.如果有,就直接使用_百 ...

发布网友 发布时间:2022-04-23 19:17

我来回答

1个回答

热心网友 时间:2022-04-12 21:11

1.类加载器深入剖析
Java虚拟机与程序的生命周期 :
当我们执行一个java程序的时候 , 会启动一个JVM进程 , 当程序执行完之后 , JVM进程就消亡了 ;
在如下情况下JVM将结束声明周期 :
System.exit(int)方法 , 当执行这个方法的时候 , 虚拟机会退出 ; 这个方法传入一个整形参数 , 这个参数是状态吗 : 如果这个整形是 0 的话 , 就是正常退出 , 如果不是0的话 , 就是异常退出 ;
程序正常结束 ;
程序执行过程中 , 遇到了异常或错误 , 而异常终止 : 如果我们在程序中出现了异常 , 而不去处理 , 会将异常一直抛给main函数 , main函数会将异常抛给JVM , JVM如果处理不了异常 , JVM就会异常退出 ;
由于操作系统出现错误导致JVM进程终止 : JVM所依赖的平台出现错误 , 导致JVM终止 ;

2.类的加载,连接和初始化
加载 : 查找并加载类的二进制数据 , 将class字节码文件加载到内存中 ;
连接 :

-
验证
: 确保被加载的类的正确性 , 使用javac 编译工具生成的字节码文件能通过验证 , 如果不是由javac编译生成的字节码文件 , 如果自己生成的字节码文件不符合JVM虚拟机对字节码文件的要求的话 , 可能会出现验证通不过的情况 ; 比如说随便拿一个文件 , 将后缀名直接修改为.class , 这样的字节码文件肯定不合法 ;

-
准备
: 为类的静态变量分配内存 , 并将其初始化为默认值 ;

-
解析
: 把类中的符号引用转为直接引用 ;
初始化 : 为类的静态变量赋予正确的初始值(正确的值指的是用户赋的值) ;
-好像这个与连接阶段的准备有些重复 , 在连接的准备阶段只是赋予初始变量 , 如果用户给这个变量赋了初始值 , 那么这个变量在连接的准备阶段仍然会赋予初始值 ;

-在这个阶段 , 才会真正的将初始值赋给静态变量 ;

Java程序对类的使用方式有 主动使用 和 被动使用 ;
所有的JVM实现 , 必须在每个类或者接口 , 被java程序 “首次主动使用” 时才初始化他们 ;
主动使用 :
创建类的实例 ;
访问某个类或接口的静态变量 , 或者对该静态变量赋值 ;
调用类的静态方法 ;
反射 : Class.forName(“类名”) ;
初始化一个类的子类 , 看做对父类的主动使用 ;
java虚拟机启动的时候 , 被标明启动类的类 , 即包含main方法的类 , 程序的入口 ;

除了上面6种主动使用之外 , 其它的情况均为被动使用 , 其它情况都不会执行第三步初始化 ;

3.类的加载
(1)概念

类的加载 : 指的是将类的.class文件中的二进制数据读入到内存中 , 将其放在运行时数据区的方法区内 , 然后再堆区创建一个java.lang.Class对象 , 用来封装类在方法区内的数据结构 ;
反射 : 反射就是跟句堆区的字节码文件 , 获取方法去的数据结构 ;
解析 : Class对象是由JVM自己创建的 , 所有的对象都是经过Class对象创建 , 这个Class对象是反射的入口, 通过Class对象 , 可以关联到目标class字节码文件的内部结构 ;

所有的类对应的Class对象都是唯一的一个 , 这个类是由JVM进行创建的 , 并且只有JVM才会创建Class对象 ;

类加载的最终产品是位于堆区中的Class对象 , Class对象封装了类在方法区内的数据结构 , 并且向Java程序员提供了访问方法区内的数据结构的接口(反射用的接口) ;

(2)加载.class文件的方式
从本地系统中直接加载 : 编译好的.class字节码文件直接从硬盘中加载 ;
通过网络下载.class文件 : 将class字节码文件放在网络空间中 , 使用URLClassLoader来加载在网络上的.class字节码文件 , 使用默认的父亲委托机制加载字节码文件 ;
从zip , jar 等压缩文件中加载字节码文件 : 在开发的时候 , 导入jar包 , 就是这种方式 ;
从专有的数据库中提取字节码文件 ;
将java源文件动态编译为字节码文件 ;

(3)类加载器
l Java虚拟机自带的类加载器 :
-根类加载器 ( Bootstrap ) : 是C++写的 , 程序员无法再java代码中获取这个类 , 如果使用getClassLoader()方法获取到的是一个null值 ;
package jvm;
Java代码
public class ClassLoaderTest {
public static void main(String[] args) throws Exception {
//java.lang包下的类使用的是跟类加载器进行加载的
Class clazz = Class.forName("java.lang.String");
System.out.println(clazz.getClassLoader());
//自定义的类使用的是应用类加载器(系统加载器)
Class clazz2 = Class.forName("jvm.C");
System.out.println(clazz2.getClassLoader());
}
}
class C{}
执行结果 :
null
Java代码
sun.misc.Launcher$AppClassLoader@1372a1a
-扩展类加载器 ( Extension ) : Java编写 ;
-系统类加载器(应用加载器) ( System ) : Java编写 ;

用户自定义的类加载器 :
-自定义的类加载器都是java.lang.ClassLoader子类 ;
-用户可以定制类的加载方式

String类是由根类加载器进行加载的 , 我们可以调用Class对象的

关于代理中创建对象的类加载器 : 创建代理对象的时候 , 动态创建一个类 , 然后使用指定的类加载器将这个类加载到内存中 , 然后用加载到内存中的类生成代理对象 ;
创建代理对象的方法 : newProxyInstance(ClassLoader loader , Class [] Interfaces , InvocationHandler h )
loader 是定义的代理类的类加载器 , 中间的接口数组是代理类的要实现的接口列表 , h 是指派方法调用的调用处理程序 ;

类加载器并不需要在某个类被 “首次主动使用” 时再加载它 :
-预加载机制 : JVM规范允许类加载器在预料某个类将要被使用的时就预先加载它 ;
-报错时机 : 如果在预加载的过程中遇到了字节码文件缺失或者存在错误的情况 , 类加载器会在程序首次主动使用(上面提到的六种情况)该类的时候报错(LinkageError错误) ;
-不报错时机 : 如果这个错误的字节码文件所对应的类一直没有被使用 , 那么类加载器就不会报告错误 ,即便有错误也不会报错 ;

LinkageError : 这个错误是Error的子类 , 程序不能处理这些错误 , 这些错误都是由虚拟机来处理 , 这个错误表示出错的是子类 , 在一定程序上依赖于另一个类 , 在编译了前面一个类的时候 , 与后面所依赖的类出现了不兼容的情况 ;
例如 : 我们使用了jdk 1.6 在编译一个程序 , 但是运行环境是jre1.5的 , 就会出现LinkageError错误 ;
4.类的连接
(1)定义
类被加载之后 , 就进入链接阶段 ; 链接 : 将已读入内存的二进制数据合并到虚拟机的运行时环境中去 ;
链接顾名思义就是讲类与类之间进行关联 , 例如我们在类A中调用了类B , 在链接过程中 , 就将A与B进行链接 ,将面向对象语言转化为面向过程语言 ;

(2)类的验证
类文件的结构检查 : 确保类文件遵从java类文件的固定格式 , 开始类的描述 , 声明 , 方法调用格式等 ;
语义检查 : 确保类本身符合java语言的语法规定 , 比如final类型的类没有子类 , final类型的方法没有被覆盖 ,在eclipse中这种错误编译的时候不能通过 , 但是通过其他的方法可以生成错误的字节码文件 , 这里就是检测恶意生成的字节码文件 ;
字节码验证 : 确保字节码流可以被JVM安全的执行 , 字节码流代表java方法(包括静态方法和实例方法) , 它是由被称作操作码的单字节指令组成的序列 , 每一个操作码后面跟着一个或多个操作数 , 字节码验证步骤会检查每个操作码是否合法 , 即是否有着合法的操作数 ;
下面是指令码组成的序列 , 类似于微指令 :

Jvm编译指令代码代码
// Compiled from ByteToCharCp1122.java (version 1.5 : 49.0, super bit)
public class sun.io.ByteToCharCp1122 extends sun.io.ByteToCharSingleByte {

// Field descriptor #17 Lsun/nio/cs/ext/IBM1122;
private static final sun.nio.cs.ext.IBM1122 nioCoder;

// Method descriptor #18 ()Ljava/lang/String;
// Stack: 1, Locals: 1
public java.lang.String getCharacterEncoding();
0 ldc <String "Cp1122"> [1]
2 areturn
Line numbers:
[pc: 0, line: 25]

// Method descriptor #2 ()V
// Stack: 2, Locals: 1
public ByteToCharCp1122();
0 aload_0 [this]
1 invokespecial sun.io.ByteToCharSingleByte() [25]
4 aload_0 [this]
5 getstatic sun.io.ByteToCharCp1122.nioCoder : sun.nio.cs.ext.IBM1122 [23]
8 invokevirtual sun.nio.cs.ext.IBM1122.getDecoderSingleByteMappings() : java.lang.String [27]
11 putfield sun.io.ByteToCharSingleByte.byteToCharTable : java.lang.String [24]
14 return
Line numbers:
[pc: 0, line: 28]
[pc: 4, line: 29]
[pc: 14, line: 30]

// Method descriptor #2 ()V
// Stack: 2, Locals: 0
static {};
0 new sun.nio.cs.ext.IBM1122 [15]
3 p
4 invokespecial sun.nio.cs.ext.IBM1122() [26]
7 putstatic sun.io.ByteToCharCp1122.nioCoder : sun.nio.cs.ext.IBM1122 [23]
10 return
Line numbers:
[pc: 0, line: 22]
}
l 二进制兼容性的验证 : 确保相互引用的类之间协调一致的 ; 例如在A类的a()方法中调用B类的b()方法 , JVM在验证A类的时候 , 会验证B类的b()方法 , 加入b()方法不存在 , 或者版本不兼容(A,B两类使用不同的JDK版本编译) , 会抛出NoSuchMethodError错误 ;

(3)准备阶段
在准备阶段 , JVM为类的静态变量分配内存空间 , 并设置默认的初始值 . 例如下面的Sample类 , 在准备阶段 ,为int类型的静态变量分配4个字节 , 并赋予初始值 0 ; 为long 类型的静态变量 b , 分配8个字节 , 并赋予初始值 0 ;
PS : 在java中基本类型变量占用的空间是一定的 , java运行在JVM上的 , 在C中 , 就要根据平台变化而变化了 ;

public class Sample {
Java代码
private static int a = 1 ;
private static long b ;
static {
b = 2 ;
}
(4)类的解析
在解析阶段 , JVM 会把类的二进制数据中的符号引用替换为直接引用 , 例如在A类中的a()方法引用B类中的b()方法 ;
在A类的二进制数据中包含了一个对B类的b()方法的符号引用 , 这个符号引用由b()方法的全名和相关的描述符组成 , 在Java解析阶段 , 就会把这个符号引用替换为指针 , 这个指针就是C语言中的指针了 , 该指针指向B类的b()方法在方法区中的内存位置 , 这个指针就是直接引用 ;

5.类的初始化
在初始化阶段 , Java虚拟机执行类的初始化操作 , 为类的静态变量赋予初始值 , 在程序中 , 静态变量初始化有两种途径 :
直接在声明处进行初始化 , 例如下面的Sample中的 变量a ;
在静态代码块中进行初始化 , 例如下面的Sample中的变量b ;

Java代码
public class Sample {
private static int a = 1 ;
private static long b ;
static {
b = 2 ;
}
}

6.面试题介绍

Java代码
public class PrepareOrInit {
public static void main(String[] args) {
Singleton singleton = Singleton.getInstance();
System.out.println(singleton.count1);
System.out.println(singleton.count2);
}
}
class Singleton{
private static Singleton singleton = new Singleton() ;
public static int count1 ;
public static int count2 = 0 ;
private Singleton(){
count1 ++ ;
count2 ++ ;
}
public static Singleton getInstance(){
return singleton ;
}
}
执行结果 : 1 0

分析 : 这段代码与类的链接中的准备阶段 和 初始化阶段 有关系 , 准备阶段是给静态的字段赋予默认值 , 初始化阶段给静态变量赋予正确的值 , 即用户的值 ;
在主函数中 , 调用了类的静态方法 , 相当于主动使用 , 这里调用了类的静态方法 ;
之后进行连接的准备操作 , 给类中的静态变量赋予初值 , singleton值为null , count1 与 count2 值为0 ;
执行初始化操作 , 给类中的静态变量赋予正确的值 , 给singleton变量赋予正确的值 , 调用构造方法 , 此时count1与 count2执行自增操作 , 两者都变成1 , 然后执行count1的赋予正确值操作 , 这里用户没有赋值操作 , count2 用户进行了 赋值为0的操作 , 0将原来的1覆盖掉了 , 因此结果为 1 , 0 ;

Java代码
public class PrepareOrInit {
public static void main(String[] args) {
Singleton singleton = Singleton.getInstance();
System.out.println(singleton.count1);
System.out.println(singleton.count2);
}
}
class Singleton{
public static int count1 ;
public static int count2 = 0 ;
private static Singleton singleton = new Singleton() ;
private Singleton(){
count1 ++ ;
count2 ++ ;
}
public static Singleton getInstance(){
return singleton ;
}
}
执行结果 : 1 1

在准备阶段count1 和 count2 都赋值为0 , 然后在初始化阶段 , 全部赋值为1 ;
编程语言java怎么运行?

Java编译一个类时,如果这个类所依赖的类还没有被编译,编译器就会先编译这个被依赖的类,然后引用,否则直接引用,这个有点象make。 如果java编译器在指定目录下找不到该类所其依赖的类的.class文件或者.java源文件的话,编译器话报“cantfindsymbol”的错误。 第二步(运行):java类运行的过程大概可分为两个过程:1...

类加载的过程

加载:加载是类加载的第一个阶段。在这个阶段,类加载器会找到并加载类的字节码文件。一般来说,这些字节码文件以.class文件的形式存储在文件系统中,也可能是从网络或其他来源获取的。类加载器首先会检查这个类是否已经被加载过,如果已经被加载,就直接返回对应的Class对象;如果没有被加载过,就读取字...

JVM内存结构

当jvm发现还没有加载过一个称为 Lava 的类 它就开始查找并加载类文件 Lava class 它从类文件中抽取类型信息并放在了方法区中 jvm于是以一个直接指向方法区lava类的指针替换了常量池第一项的符号引用 以后就可以用这个指针快速的找到lava类了 而这个替换过程称为常量池解析(constant pool resolution) ...

JVM详解基础篇之执行引擎(玩转Java,游刃有余)

当一个方法被调用执行时,会首先检查该方法是否已经被JIT编译过了,如果是的话,则直接执行上次编译后生成的本地机器码。反之,如果还没有编译,则先对方法调用计数器+1,然后判断计数器是否达到了规定的阈值,如果还未达到阈值标准则采用解释器的模式执行代码。如果达到了规定阈值则提交编译请求,由JIT负责后台编译,后台线程...

Hotpot Java虚拟机Class对象是在方法区还是堆中

Class类的引用 每个加载的类型对应一个Class实例,JVM通过此连接方法区中的类型数据。方法表 方法表优化访问效率,为方法提供直接引用,加快激活实例方法的速度。例子展示方法区使用 通过类名加载并执行代码,JVM从方法区获取类型信息,解析字节码激活方法。总结 Class对象存储在Java堆中,而非方法区。方法区...

JVM方法区内存中 存储的是 .class字节码文件么?

不是字节码文件,内存中存储的是,在类被引用(调用)时,JVM通过ClassLoader动态加载class文件形成的对象。

JVM内存深度介绍

已被加载的类的static域引用的对象 方法区中常量引用的对象 JNI引用 以上不完全,但是已经够用了。 可达性分析:通过一系列的“GCroots”对象作为起点搜索。如果在“GCroots”和一个对象之间没有可达路径,则称该对象是不可达的。要注意的是,不可达对象不等价于可回收对象,不可达对象变为可回收对象至少要经过两次标记...

...静态方法,静态属性)是程序一运行就加载到jvm中,还是当被调用的时候...

1、类中的静态属性会被加入到类对象(也可以叫做类的模板,是类的描述) 的构造器中,静态方法也会被加入到类对象中。2、当第一次使用类时,JVM会通过类加载器,加载类对象,从而初始化静态属性,并装入类的方法,包括静态方法和实例方法(方法不会被调用,只是加载,从这个意义上来说,静态方法和...

深入synchronized底层原理

Monitor对象存在于每个Java对象的对象头Mark Word中(存储的指针的指向),Synchronized锁便是通过这种方式获取锁的,也是为什么Java中任意对象可以作为锁的原因,notify/notifyAll/wait等方法会使用到Monitor锁对象,所以必须在同步代码块中使用。 刚开始时 Monitor 中的 Owner 为 null 当Thread-2 执行 synchronized(obj){}...

java线程存放在jvm的哪个区域?方法又存放在哪个区呢?

聊到JAVA中的方法,大多数人对于方法存储在方法区还是栈区(虚拟机栈)是很迷茫的。其实方法是存在方法区的下面我们就细细说一下JVM中的 方法区 VS 栈区方法区:用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据,方法编译出的字节码也是保存在这 ...

声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。
E-MAIL:11247931@qq.com
...设计方面的知识,请各位朋友指教下,该学习哪些东西或推荐些书籍,十分... 要独立完成一个网站需要学习哪些软件和知识? ...在一般公司做网络维护之类的,求指教应该学习哪些软件或书籍_百度知 ... 我想学点联网知识,在一个企业能够胜任网络维护工作!请指教. 我刚接手一个网站维护的工作,到底我该怎么去维护,从何入手啊?求大神指... 匡威有哪些款式的休闲鞋值得入手? 硅钙板价格多少硅钙板价格多少一平米 匡威什么颜色最值得买 匡威最值得入手的鞋子 山海旅人土地公:神话故事中的土地神 《中国的土地》中,诗人为什么说中国的土地是“神奇的? 哪里有java程序员面试题啊,谢谢 C++语言与Java语言的区别有哪些?(面试题) 软件测试员面试为什么还需要了解Java虚拟机 学习java有必要看 深入jvm 吗 java web面试 Java面试题:什么是Java虚拟机?为什么Java被称作是“平台无关的编程语言”? java面试,问性能调优从哪些方面着手 请问各位JAVA面试都问一些什么问题? 学Java的面试都会问些什么? 初级java面试,面试官是如何出题的 java基础面试题有哪些? 苹果7无通话声音,这个指令是什么意思? iphone7plus代码是多少? 我的苹果7快捷指令系统设置在那 苹果怎么找所有快捷指令方式 苹果7没有快捷指令吗 财信人寿保险公司怎么下载 农银人寿保险app下载? 如何把一台手机上的哔哩哔哩视频转移到另一台手机上? 怎样用手机哔哩哔哩上传视频?? 面试Java虚拟机 有什么用 深入java虚拟机,*和深入理解Java虚拟机:JVM高级特性与最佳实践。 梦见自己抱住大树差点掉海里 梦到要掉到海里,什么意思 梦到自己坐在船上,差点掉到海里?? 青少年做梦梦见自己下差点掉海里 昨天晚上梦见掉河里了,什么征兆呢? 梦见自己差点从桥上跳下海,海里还有鱼? 梦见自己差点被风刮进大海里 奕字取名寓意好吗? 姓姚,男孩,农历2021年三月二十日上午十点二十分出生,求取名!中间带奕字,姚奕? 为什么要忌讳奕字取名? 起名字姓黄男孩戴个奕字 姓闫,男孩,带个奕字,取什么名好啊? 姓韦带奕字取名,男孩 取个带奕字的名字,我孩子是男孩一二年正月初七生的 起名字,名字中间的字“奕”,求名字中最后一个字,赖奕()希望好听点,寓意也好的,男孩,今年龙年生 李姓起名中间要带奕字辈 况姓“道”字辈女孩起名 况氏女孩取名!妈妈姓李!烦请高手相助
  • 焦点

最新推荐

猜你喜欢

热门推荐