类生命周期
从类加载到初始化
1、加载:读取class文件二进制字节码内容(加载到方法区)
2、验证:class文件格式规范、语义分析、引用验证、字节码验证
3、准备:分配内存、设置类static修饰的变量初始值
4、解析:类信息、类、接口、方法、字段等解析
5、初始化:为静态变量赋值、执行静态代码块
6、使用:创建实例对象
7、卸载:从JVM方法区中卸载
方法区:
JVM用来存储加载的类信息、常量、静态变量、编译后的代码的数据。
虚拟机规范中这是一个逻辑区划,具体实现根据不同虚拟机来实现,如:oracle的HotSpot在Java7中方法区放在永久代,Java8放在元素据空间,并且通过GC机制对这个区域进行管理
类如何加载到方法区?
通过类加载器:加载网络、jar、Zip、文件夹、二进制数据、内存等指定位置的类资源
运行一个程序,至少3个类加载器实例,负责不同类的加载:
1、Bootstrap loader(核心类库加载器):C/C++实现,加载JDK核心类库(lib目录、rt.jar、java.lang.String ...)
2、Extension class loader(扩展类库加载器):扩展类库(ext目录)
3、Application class loader(用户应用程序类加载器):加载java.class.path指定的目录、用户应用程序classpath
查看类对应的加载器:
通过jdk-api:java.lang.class.getClassLoader(),该方法返回装载类的类加载器
如果该类(如:String类)是由Bootstrap loader加载器加载的,则改方法返回null,因为Bootstrap loader加载器是C/C++实现,负责加载Java程序运行的必备类库
JVM如何知道我们的类在哪里?
通过java.class.path配置指定去哪些目录加载类资源
验证过程:利用jps、jcmd两个命令
jps:查看正在运行的JVM
jcmd:让正在运行中的jvm执行指令
e.g:jcmd 进程号 VM.system_properties
AppClassLoader默认读取java.class.path位置的类
URL jarUrl = new URL("file:\\D:\\demo.jar");
URLClassLoader classLoader = new URLClassLoader(new URL[]{jarUrl});
类重复加载判断:类加载器实例id+类路径+类名
tomcat类热加载原理:当文件有变化,创建一个新的类加载器实例重新加载(类路径+类名一般不会变)
查看类加载、卸载日志的JVM运行参数:--verbose:class
类卸载:
条件:
1、该class所有的实例都已被GC
2、加载该类的classloader实例被GC了
双亲委派模型:
URL jarUrl = new URL("file:\\D:\\demo.jar");
URLClassLoader classLoader1 = new URLClassLoader(new URL[]{jarUrl});
URLClassLoader classLoader2 = new URLClassLoader(new URL[]{jarUrl}, classLoader1 );
为了避免重复加载,由下到上逐级委托,由上到下逐级查找
首先不会自己去尝试加载类,而是把这个请求逐层委派给父加载器去完成
只有当父加载器反馈自己无法完成该加载请求(该加载器的搜索范围中没有找到对应的类)时,子加载器才会尝试自己去加载
注:类加载器之间不存在父类之类的关系,只是逻辑上定义的上下级关系
总结:
1、JVM知识分多个层级,知晓-领悟-熟练运用
2、当知识点被梳理清晰之后,学习方向会更加明确
3、合适的学习方法,能节约大量时间