类加载

1*.java文件 ->编译器生
2*.Class字节码文件->类加载器(类加载:链接-初始化)
3实例化(1new方法,2反射,3调用类的静态方法,4调用某个类的接口或者静态变量,5Java指定的启动类)
4使用
5卸载(清除该类的实例1;清除该类classLoader的应用2;清除该类对象的应用3)

知识点
Class字节码生成动态代理类的字节码文件 ->通过类加载器->加载到jvm内存

动态代理是在运行期使用字节码生成技术动态生成代理类的字节码文件的,然后被类加载器加载到 JVM 里。
JDK 动态代理要求目标类必须实现接口,因为生成的代理类要继承 Proxy 类来获得增强方法的类的处理,所以代理类必须要实现和目标类同样的接口。
CGLib 动态代理不要求目标类是否实现接口,是因为 CGLib 生成的代理类继承了目标类。
Spring AOP 的本质就是动态代理,使用 JDK 或 CGLib 动态生成代理类的字节码文件。

#1方法区(常量池)
法区内主要存储了一些类信息,例如字段、方法、常量池,相当于记录了一个类的结构组成,当你new一个对象的时候,jvm就会根据你这个类在方法区内的数据结构在堆中构建一个新的对象,类似于记录了一个模型,每次的新建必须根据这个模型来构造。
这里面的常量池有一点特殊,常量池里记录了不会变更的常量,比如接口名称,类名称、字段名称、方法名称等等以符号出现的形式,除此之外所有的字符串都会记录在常量池里,每次对字符串的引用如果常量池里有都会从常量池获取,初次之外,加关键字final的属性也会存储在常量池中,所以这也从侧面映射了常量池中的属性是不允许更改的,且是永久的,所以占用内存较大。
2堆
堆是存储对象的地址空间,每个新对象的构造都需要在堆上开辟空间。由于我们需要不断的在堆上构造新的对象,而且对象的大小不统一,所以会造成堆的严重浪费和重复利用率低。但是java比C++最大的优势有GC(垃圾回收)机制,jvm会监测堆中内存占用大小,阈值,那些空间是不会再利用了,那些空间还需要继续使用,怎样压缩空间全都可以交给gc来管理了。所有有用的对象会直接或间接和一个root指针建立连接,否则会在下一次gc判断为垃圾进行空间回收。

3虚拟机栈
本地方法栈从内存方面和java虚拟机栈类似,唯一不同的是本地方法栈是站在操作系统层面的,不属于jvm的范畴,至于这样设计的主要原因我想是因为要给java留有调用本地C代码的口子。

4本地方法栈

5程序计数器


由内而外的类加载过程解析https://mp.weixin.qq.com/s/XdtEdleazkfU9awgN2WRdw

#java自带的类加载器:
#1启动类加载器(Bootstrap ClassLoader)
负责加载 JAVA_HOME\lib 目录中的,或通过-Xbootclasspath 参数指定路径中的,且被虚拟机认可(按文件名识别,如 rt.jar)的类。

#2扩展类加载器(Extension ClassLoader)
负责加载 JAVA_HOME\lib\ext 目录中的,或通过-Djava.ext.dirs 系统变量指定路径中的类 库。

#3应用程序类加载器(Application ClassLoader):
负责加载用户路径(Class-Path: )上的类库。一般我们编写的java类都是由这个类加载器加载
也就是 BOOT-INF 下的 lib 目录考出,设置指定目录-Dloader.path="lib/"
Main-Class: org.springframework.boot.loader.JarLauncher
Start-Class: com.ccb.dkemp.configservice.ConfigServiceApplication
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/

#4实现自定义的类加载器。
JVM 通过双亲委派模型进行类的加载,当然我们也可以通过继承 1:java.lang.ClassLoader;2:重写父类的 findClass() 方法

#双亲委派模:一个类加载器收到了类加载请求,它不会尝试自己去加载这个类;
委派给父加载器类去完成,每一个层次类加载器都是如此,因此所有的加载请求都应该传送到启动类加载器中;

启动例子
-Dloader.path

java -Xbootclasspath/a:./config -jar springboot-start-0.0.1.jar -DJava.ext.dirs=./lib   >> springboot.log  &

java -Xbootclasspath/a:./config -Dloader.path=./lib -jar springboot-start-0.0.1.jar  >> springboot.log  &

#JVM 类加载机制分为五个部分:加载,验证,准备,解析,初始化
#1加载(初次加载类时):
操作过程如下:
1.通过全限定类名(包名+类名)来获取此类的二进制字节流。
2.将这个类的字节流中静态存储结构转化到方法区中的数据结构。
3.在jvm堆中生成一个代表这个类的 java.lang.Class 对象,作为方法区这个类的各种数据的访问入口。

#2验证:
为了确保Class文件的字节流中的信息是否符合当前虚拟机的格式,不会危害虚拟机的安全,主要包含以下几部分:文件格式的验证,元数据验证,字节码验证,符号引用验证。

#3准备:
准备阶段是为类变量(static 类型)分配内存并设置类变量的初始值的阶段,就是在方法区中分配这些变量所使用的内存空间。注意:实例变量不在此阶段分配内存空间,因为是在类对象实例化时分配内存到堆中。
这里说的是初始值(该数据类型的初始值)而非初始化,比如一个类变量定义为:
public static int a = 123;
变量a在准备阶段过后的初始值为0而不是123,将a赋值为123的put static指令是程序被编译后,存放于类构造器<client>方法之中。
数据类型对应的初始值如下所示:
int    0
long 0L
boolean  false
float   0.0f
short  0(short)
double 0.0d
注意:
如果声明为常量,如:
public static final int a = 123;
那么在准备阶段虚拟机会根据ConstantValue(常量)属性将a赋值为 123。


#4解析:
解析阶段是指虚拟机将常量池中的符号引用替换为直接引用的过程。
符号引用:
符号引用,是指变量的代号,符号引用的字面量形式明确定义在 Java 虚拟机规范的 Class 文件格式中。
直接引用:
直接引用,可能是是指向目标的指针(或相对偏移量)或者是一个能间接定位到目标的句柄。如果有了直接引用,那引用的目标必定已经在内存中存在

初始化:
初始化阶段,是类加载的最后一个阶段,类加载阶段之中,除了在加载阶段可以自定义类加载器以外,其它操作都由jvm主导。在初始化阶段,开始真正执行类中定义的 Java 程序代码。初始化阶段主要使用<clinit>() 方法,执行类变量(static)的初始化及静态代码块执行。
初始化过程先父后子,子类初始化必须父类先初始化。
<client>方法是由编译器自动收集类中的类变量(static类型)的赋值操作和静态语句块中的语句合并而成的。

#5类初始化时机有如下几种:
1创建某个类的实例,就是使用new的方式实例化类。
遇到new、getstatic、putstatic、或 invokestatic这四条字节码指令时,如果类没有进行初始化,则需要先触发其初始化。生成这4条指定最常见的Java代码场景:使用new关键字实例化对象的时候、读取或设置一个类的静态字段、(被final修饰、已在编译期把结果放在常量池的静态字段除外。)

2访问某个类或接口的静态变量,或者对该静态变量赋值
3调用类的静态方法
4使用反射实例化某个类(如Class.forName(“”))
5初始化某个类的子类,其父类也会被初始化
Java虚拟机启动时被标明为启动类的类(main方法的类),直接使用main方法来运行


#1类的初始化步骤
1假如这个类还没有被加载和连接,那就先进行加载和连接。
2假如类存在直接父类,并且这个父类还没有被初始化,那就先初始化直接父类。
4假如类中存在初始化语句,那就依次执行这些初始化语句。
知识点
import org.tes.A 导入类的作用
编译是会变成 org.tes.A a = new org.tes.A();
import给编译器用,编译之后,import无作用,和类加载无关;
详谈类加载的全过程https://mp.weixin.qq.com/s/DPZpb_nHd8X15G-_4-wVBg

#被动引用【不会引起初始化】
1、通过子类来引用父类的静态字段,只会触发父类的初始化,不会触发子类的初始化。
2、通过数组定义来引用类,不会触发此类的初始化。SuperClass[] superClasses = new SuperClass[10]
3、引用一个类的静态常量也不会触发初始化,因为常量在编译阶段已经确认。public static final int value = 22;

static 类加载默认=0


#实例化顺序
先进行类初始化,再执行实例初始化
1父类静态初始化块
2子类静态初始化块

3父类非静态初始化块
4子类非静态初始化块

5父类构造方法
6子类构造方法


#3使用

#4卸载


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值