java文件如何被运行
先通过编译,将.java文件编译成.class文件,然后让类加载器加载到方法区中去
类加载的内存分析
JVM内存结构
方法区:
- 用于存放类似于元数据信息方面的,比如类信息,常量,静态变量,编译后代码……
- 类加载器将**.class文件**搬过来就是先丢到这一块上
堆:
- 主要存放了一些存储的数据,比如对象实例(包括class对象实例),数组等
- 它和方法区都属于线程共享区域,也就是说它们的线程不安全
栈:
- 这是我们的代码运行空间。我们编写的每一个方法都会放到栈里面运行
- 线程独享
类加载器的流程
加载:
- 将.class文件加载到内存
- 将静态数据结构转换为方法区中运行时的数据结构
- 在堆中生成一个代表这个类的java.lang.Class对象作为数据访问的入口
链接:
- 验证:确保加载的类符合JVM规范和安全,保证被校验类的方法在运行时不会做出危害虚拟机的事件,其实是一个安全检查
- 准备:为static变量在方法区中分配内存空间,设置变量的初始值
- 解析:虚拟机将常量吃内的符号引用替换为直接引用的过程
初始化:
- 执行类构造器
()方法。类构造器方法是由编译期自动收集类中所有的类变量的赋值动作和静态代码块中的语句合并产生的。
分析类的初始化
什么时候会发生类初始化?
- 类的主动引用(一定会发生类的初始化)
- 当虚拟机启动,先初始化main方法所在的类
- new一个类的对象
- 调用类的静态成员(除了final常量)和静态方法
- 使用java.lang.reflect包的方法对类进行反射调用
- 当初始化一个类,如果其父类没有被初始化,则会先初始化他的父类
- 类的被动引用(不会发生类的初始化)
- 当访问一个静态域时,只有真正声明这个域的类才会被初始化。如当通过子类引用父类的静态变量,不会导至子类初始化
- 通过数组定义类引用,不会触发此类的初始化
- 引用常量不会出发此类的初始化(常量在链接阶段就存入调用类的常量池中了)
类加载器
类加载的作用:
将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口(注意:入口在堆中)
类加载器的加载顺序
加载一个Class类的顺序也是有优先级的,类加载器从最底层开始往上的顺序是这样的
BootstrapClassLoader(启动类加载器)
负责加载
$JAVA_HOME中jre/lib/rt.jar
里所有的class,加载System.getProperty(“sun.boot.class.path”)所指定的路径或jarExtensionClassLoader(标准扩展类加载器)
负责加载java平台中扩展功能的一些jar包,包括$JAVA_HOME中jre/lib/*.jar或-Djava.ext.dirs指定目录下的jar包。载System.getProperty(“java.ext.dirs”)所指定的路径或jar。
AppClassLoader(系统类加载器)
负责记载classpath中指定的jar包及目录中class
CustomClassLoader(自定义加载器)
属于应用程序根据自身需要自定义的ClassLoader,如tomcat、jboss都会根据j2ee规范自行实现。
双亲委派机制
当一个类收到了加载请求时,它是不会先自己去尝试加载的,而是委派给父类去完成。
只有当父类加载器范阔自己无法完成这个请求(也就是父类加载器都没找到加载所需的Class)时,子类加载器才会自行尝试加载
好处:加载位于rt.jar包中的类时不管是那个加载器加载,最终都会委托到BootStrap ClassLoader进行加载,这样保证了使用不同的类加载器得到的都是同一个结果。
其实这是一个隔离的作用,避免了我们的代码影响了JDK的代码。
类缓存
某一个类被类加载器加载到内存中,维持加载(缓存)一段时间。不过JVM垃圾回收机制可以回收这些Class对象