【JVM】Java类加载机制这块算是玩明白了
空門蒼の稻荷:
jvm类加载分三个阶段
加载:将jvm字节码文件加载到内存中,jvm使用双亲委派机制保护jvm内部的安全,以及防止类的重复加载
链接:链接又分为三个阶段
1.验证:主要验证加载的文件是否符合jvm规范
2.准备:为类变量赋予零值,类变量如果被final修饰则直接赋予定义的值
3.解析:将符号引用转化为直接引用
初始化:为类变量赋值,为成员变量赋零值,执行静态代码块
jvm主要分为五块区域,堆,虚拟机栈,本地方法栈,pc寄存器,元空间(jdk7是永久代,用于存放类信息,字符串常量池,静态变量。jdk8以及以后元空间存放类信息,字符串常量池和静态变量被放到了堆中。无论是元空间还是永久代,都只是方法区的一种实现。jrocket虚拟机是没有方法区的,hotspot有方法区,在jdk8中整合两大虚拟机之后,由永久代换成了元空间。此后虽然大的模块没有发生变化,但肯定已经有很大变化了,我猜的[OK]。现在的视频讲的大多是7和8的东西,跟不上时代的步伐了呀[doge])
【回复】后半部分说的好像不太对,“jvm主要分为五块区域,堆,虚拟机栈,本地方法栈,pc寄存器,元空间”,其中“元空间”应该改为“方法区”。jrocket中有方法区,只是没有永久代这种实现方式
【回复】方法区是JVM规范中的定义,永久代和元空间都是它的实现
【回复】跟不上时代,笑曰了。java 虚拟机不管怎么发展都是向下兼容的。优化的方向不是类的加载机制而是内存管理。你看不出来。
油馍大师i:
在视频10:30处,JVM的解析阶段使用invokedynamic来实现多态,是错误的。这个解析过程使用的是invokevirtual指令来实现。invokedynamic指令是用来实现动态类型特性的。invokevirtual是用来实现重写(动态分派);invokedynamic的典型用法就是lambda表达式。详见《深入理解JAVA虚拟机》8.3章节[W-哈哈]
【回复】嗯 这边讲错了,用的是invokevirtual或invokeinterface
【回复】视频看到这的时候我还以为我记错了[藏狐]
减不到130不改名:
类加载的过程:
1 加载
1.1 将类的二进制字节码载入JVM中
1.2 将二进制字节码转换为方法区所理解的数据结构
1.3 在堆中生成一个java.lang.Class 对象,表示堆方法区中类的引用
2 连接
2.1 验证:保证载入的类不会危害JVM
2.2 准备:给静态变量常量设置默认值
2.3 解析:将类中的符号引用转化为直接引用
3 初始化:初始化静态常量和成员变量
look4you__:
还没看就三连了 请务必坚持下去[打call]
卡巴内瑞丶y:
首先是**加载**
加载过程是读取class文件,其中class文件是经过javac编译过后字节码文件。然后这个class文件也不仅仅只来源于本地,也可以来源于数据库,网络,或者及时生成的class文件,比如动态代理技术就是通过这个接口实现的。加载过程就是将这个字节码文件转化为某种静态数据结构储存在方法区中,并且在堆内生成一个便于用户调用的实例对象。
加载之后,虽然JVM内存中堆里面已经存在了这个对象,但是这个时候这个对象并不被JVM所认可,要想能够调用这个类,JVM需要对这个类进行连接
**连接**
连接的**第一步**就是**验证**,验证顾名思义,就是对这个对象进行检验,检验的内容大概分为以下几点,首先是对文件格式的检验,以确保这是一个可加载的一个class文件,这样一步在加载过程之前就已经完成了。然后是对元数据和字节码的验证,目的是对class静态结构进行语法和语义上的分析,以确保不会产生危害虚拟机的动作。还有一个验证是符号引用的验证,这个验证其实是发生在解析阶段。可以看到,验证的内容有四个,文件格式的验证,元数据和字节码的验证,符号引用的验证,但是这四个验证并不是紧密连接在一起的,所以这是这个图的一个问题。也就是说,验证包含了很多个步骤,分散在不同的阶段内。随着虚拟机的不断发展,相关的开发人员可能会在虚拟机的验证过程加入更多的相关验证机制。
【回复】连接的**第二步**就是**准备**,准备其实就是把对象的静态变量赋0值
连接的三步就是解析,解析阶段做的事情是,**把符号引用替换为直接引用**。这里面的符号引用和直接引用,我谈谈我的理解:比如一个类A,它引用了B这个类,在编译阶段,A这个类是不知道B有没有被加载的,也不知道B的实际地址,这个时候在A的class文件中将会用一个字符串来代表B的地址。这个字符串就被称为符号引用。在运行的时候,如果触发了A的类加载,到解析阶段发现B没有加载,这个时候,会触发B的类加载,此时A中的字符串会被B的地址所代替,这就叫直接引用。
解析又分为静态解析和动态解析,因为Java有多态机制,如果上面提到的B是一个实体类,那么这样的解析称为静态解析,如果B是一个接口或者是抽象类的时候,这个时候就没有办法确定这个引用的实际地址,既然没有那就先留着。等到运行阶段发生了调用,这个时候虚拟机中的调用栈将会得到具体的类型信息,这个时候再进行解析就可以得到明确的直接引用。这个过程就叫做动态绑定。这也是为什么解析阶段会发生在初始化阶段之后,实现后期绑定。
最后是初始化阶段
初始化阶段就很简单了,先判断代码中是否存在主动资源初始化操作,如果有的话,那么执行。主动资源初始化动作是指class层面的一些静态代码块,成员变量的赋值操作。 肯定是不包括构造函数的,构造函数是对象层面的,这个class是类层面的。
【回复】回复 @这个柠檬有点酷 :有的解析发生在运行阶段,有的解析发生在编译阶段,在编译阶段,接口与抽象类不知道谁实际引用了它们,等到运行阶段,发生了实际的调用,然后就会根据调用栈的信息去加载相应的接口,实现明确的直接引用。这个是发生在运行阶段的解析。而比如一些实体类,在编译阶段就会完成解析,因为他们都有明确的地址。这个是发生在编译阶段的解析
【回复】回复 @卡巴内瑞丶y :等到运行阶段发生了调用,这个时候虚拟机中的调用栈将会得到具体的类型信息,这个时候再进行解析就可以得到明确的直接引用。这个过程就叫做动态绑定。这也是为什么解析阶段会发生在初始化阶段之后,实现后期绑定???解析不就是发生在运行阶段而不是编译阶段吗?
B站舞蹈区老油子:
好气 看完了 发现最想看的在下一期[doge] (毕竟出bug 需要分析的在下一期[doge])
java微服务:
谁能给我解答一下,类是什么时候加载的,是一下加载所有的类,还是加载一部分然后接下来用到哪个再加载哪个呢?加载到哪里?内存还是jvm,如果是jvm,那么是jvm的哪一块???谁能解释一下?
【回复】所有的类想要使用都必须加载,一部分是启动时就加载,一部分是用到的时候再加载。加载到堆内存当中,生成一个代表这个类的java.lang.Class对象便于用户调用。类加载有五种方式
1.new 关键字
2.调用静态方法
3.调用静态属性
4.java1.8版本以后,接口可以通过加defalut关键字实现类加载
5,反射
6.子类触发父类的静态代码
7. 程序的入口main
阿汤青叔:
有个地方讲错了,成员变量 和 类变量 不是平级关系。成员变量 分为 "实例变量" 和 "类变量"
【回复】回复 @寒食君 :是的,成员变量也叫属性, 由单词field单词翻译而来。早期有些书籍将这个单词翻译成 "属性"。这些都是人定的名词其实纠结这个也没什么意义[吃瓜]
【回复】好好再理解一下,你这个说法不对
【回复】成员变量就是属性吧,类变量是静态变量
昵称不存在酱:
最后初始化的时候,成员变量也会初始化吗?还是说只有类变量和静态代码块中的内容
【回复】成员变量是在创建对象时初始化的
挖掘开源的价值:
4把看视频写的笔记生成在线站点了,供大家学习 https://shaoxiongdu.github.io/JVMStudy/#/
hypnos:
视频末尾提到“加载二进制流和初始化阶段开放主导权给用户,可以自由控制。”,想问下这里的“用户自由控制”具体是什么意思?
【回复】用户可以自定义类加载器
蓝色暹罗猫儿:
11:10 成员变量的赋值动作是init方法调用,并非clinit方法调用
守夜人枫零:
内容挺好,但这字幕做的……疯狂闪轴……
旋转又旋转:
还想翻up的视频记录想找下一期,没想到这个居然就是最新了,三连催更了快快更新!
月才上:
类加载过程:加载、链接(验证、准备、解析)、初始化。非常感谢,加深了印象,对符号引用和直接引用有了大体的认识。
读书且快乐:
投币支持了,UP速更[星星眼][星星眼][星星眼]
小亮哥哥的世界:
请问,哪些大厂会花30k/月招人?🤔🤔🤔
【回复】回复 @寒食君 :30k一个月