同一个JVM的有线程、所有变量都处于同一个进程里,他们都使用JVM进程的内存区。当系统出现以下几种情况时JVM进程将被终止:
1、程序运行到最后正常结束
2、程序运行到使用System.exit()或Runtime.getRuntime().exit)代码处结束程序。
3、程序执行过程中遇到未捕获的异常或错误而结束。
4、程序所在平台强制结束了JVM进程
注意两次运行java程序,处于两个不同的JVM他们不共享内存。
类的加载
1.当程序主动使用某个类时,如果该类还未加载到内存中,则系统会通过加载、连接、初始化三个步骤来对该类进行初始化。这三个步骤统称类加载或类初始化。
2.类加载 指类的class文件读入内存,并为之创建一个java.lang.Class对象,也就是说,当程序中使用任何类时,系统都会为之建立一个java.lang.Class对象。
3.类是某一类对象的抽象,是概念层次的东西,其实类也是一种对象,系统中所有的类实际上是实例,它们都是java.lang.Class的实例
4.类的加载由类加载器完成,类加载器通常由JVM提供,JVM类加载器通常称为系统类加载器。除此之外,也可以通过继承ClassLoader基类来创建自己的类加载器
5、通过使用不同的类加载器,可以使用不同的来源加载类的二进制数据,通常来源有:
①从本地文件系统加载class文件,是绝大多数的加载方式
②从jar包加载class文件
③通过网络加载class文件
④把一个java源文件动态编译,并执行加载
类加载器通常无须等到“首次使用”该类时才加载该类,Java虚拟机规范允许系统预先加载某些类
类连接
当类加载之后,系统为之生成一个Class对象,接着进入连接阶段,此阶段负责将类的二进制文件数据合并到JRE中,
1、验证:检验加载的类是否有正确的内部结构,并和其他的类协调一致
2、准备:负责为类的变量分配内存,并设置默认初始值。
3、解析:将类的二进制数据中的符号引用替换成直接引用
类的初始化
1、类的初始化阶段,虚拟街负责对类进行初始化,主要是对类的变量进行初始化。
①声明变量时指定初值
②使用静态初始化块为类变量指定初始值。
2、声明变量初始值时,静态初始化块都将被当成类的初始化语句,JVM会按照这些语句在程序中的排列顺序依次执行他们。
public class JavaReflectLearning { static int a = 5; static int b = 9; static int c; static{ b = 6; } public static void main(String[] args){ System.out.println(JavaReflectLearning.b); } }
输出 6
public class JavaReflectLearning { static{ b = 6; } static int a = 5; static int b = 9; static int c; public static void main(String[] args){ System.out.println(JavaReflectLearning.b); } }
输出是 9
3、类初始化步骤
①假如这类还没有被加载和连接,则程序先加载并连接该类
②假如该类的直接父类还没有被初始化,则先初始化其直接父类(递归到java.lang.Object)
③假如类中有初始化语句,则系统依次(按顺序)执行这些初始化语句
类的初始化时机
1、当Java程序首次通过下面6种方式来使用某个类或接口时,系统就会初始化该类或接口:
①创建类的实例。创建实例方式包括 : new操作符来创建实例, 通过反射来创建实例 , 通过反序列化方式来创建实例
②调用某个类的类方法(静态方法);
③访问某个类或接口的类变量,或为该类变量赋值
④使用反射方式来强制创建某个类或接口对应的java.lang.Class对象。如 Class.forName ("Person"), 如果系统还未初始化Person类,这行代码会导致Person类被初始化,并返回Person类对应的java.lang.Class对象。
⑤初始化某个类的子类。当初始化某个类的子类时,该子类的所有父类都将会被初始化
⑥直接使用java.exe 命令来运行某个主类。当运行某个主类时,程序会先初始化该主类。
2、对于final型的类变量,Java编译器会在编译时直接把这个类变量出现的地方替换成它的值,因此即使程序使用该静态类变量,也不会导致该类的初始化。
当某个类变量(静态变量)使用了final修饰,而且它的值可以在编译时就确定下来,那么程序其他地方使用该类变量时,实际上并没有使用该类变量,而是相当于使用常量。
public static final String compileConstant = "learning java reflect";
//当访问、输出MyTest中的comlipleConstant时 并不会导致类的初始化
System.out.println(JavaReflectLearning.compileConstant);
3、如果final修饰的类变量不能在编译时确定下来,则必须等到运行时才可以确定该类变量的值,当该类来访问它的类变量时,则会导致该类被初始化。
public static final String compileConstant1 = "" + System.currentTimeMillis();
4、当使用ClassLoader类的loadClass()方法来加载某个类时,该方法只是加载该类,并不会执行该类的初始化。使用Class的forName() 静态方法才会导致强制初始化该类。
public class ClassLoaderTest { public static void main(String[] args) throws ClassNotFoundException{ ClassLoader cl = ClassLoader.getSystemClassLoader( ); //仅仅是加载Tester类 cl.loadClass("包名.Test"); System.out.println("系统加载Tester"); //下面语句才是初始化Tester Class.forName("包名.Test"); } }
类加载器
1、类加载器负责将.class文件(磁盘或网络)加载到内存中,并为之生成对应的java.lang.Class对象。一旦一个类被载入JVM中,同一个类就不会被再次载入了。
2、正如一个对象有一个唯一的标识一样,一个载入JVM类也有一个唯一的标识。在Java中,一个类用其全限定类名(包括包名和类名)作为标识;但在JVM中,一个类用其全限定类名和其类加载器作为其唯一标识。
3、当JVM启动时,会形成由三个类加载器组成的初始类加载器层次结构。
①Bootstrap ClassLoader:根类加载器
②Extension ClassLoader:扩展类加载器
③System ClassLoader:系统类加载器
4、Bootstrap ClassLoader被称为引导(原始或根)类加载器,负责加载Java的核心类。在Jvm中,当执行java.exe时,使用-Xbootclasspath选项或使用-D选项指定sun.boot.class.path系统属性值可以指定加载附加类
5、根加载器非常特殊,它并不是java.lang.ClassLoader的子类,而是有JVM自身实现。
public class BootstrapTest { public static void main(String[] args){ URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs(); for(URL url:urls){ System.out.println(url.toExternalForm()); } } } 输出:
file:/C:/Program%20Files/Java/jdk1.7.0_80/jre/lib/resources.jar file:/C:/Program%20Files/Java/jdk1.7.0_80/jre/lib/rt.jar file:/C:/Program%20Files/Java/jdk1.7.0_80/jre/lib/sunrsasign.jar file:/C:/Program%20Files/Java/jdk1.7.0_80/jre/lib/jsse.jar file:/C:/Program%20Files/Java/jdk1.7.0_80/jre/lib/jce.jar file:/C:/Program%20Files/Java/jdk1.7.0_80/jre/lib/charsets.jar file:/C:/Program%20Files/Java/jdk1.7.0_80/jre/lib/jfr.jar file:/C:/Program%20Files/Java/jdk1.7.0_80/jre/classes
6.Extension ClassLoader扩展类加载器,负责加载JRE的扩展目录(%JAVA_HOME%/jre/lib/ext)或者由java.ext.dirs系统属性指定的目录中的JAR包类
通过这种方式就可以为Java扩展核心类以外的新功能,只要将自己开发的类打包成JAR文件然后放到%JAVA_HOME%/jre/lib/ext即可。
7、System ClassLoader是系统(应用)类加载器,它负责在JVM启动时加载来自java命令的-classpath、java.class.path系统属性,或CLASSPATH环境变量所指定的JAR包和类路径。程序可以通过ClassLoader的静态方法getSystemCLassLoader()来获取系统类加载器。
类加载机制
1。JVM的类加载机制主要有三种
①全盘负责, 当一个类加载器负责加载某个Class时,该Class所依赖的和引用的其他Class也将由该类加载器负责载入,除非显式使用另外一个类加载器来载入。
②父类委托, 先让parent(父)类加载器视图加载该Class,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类。(类加载器之间的父子关系并不是类继承上的父子关系,是类加载器实例之间的关系)
③缓存机制, 保证所有加载过的Class都会被缓存,当程序中需要使用某个Class时,类加载器先从缓存区中搜寻该Class,只有当缓存区中不存在该Class对象时,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存入缓存区总。这就是为什么修改Class后,必须重启JVM,程序所做的修改才会生效的原因。
import java.io.IOException; import java.net.URL; import java.util.Enumeration; public class ClassLoaderPropTest { public static void main(String[] args) throws IOException{ ClassLoader systemLoader = ClassLoader.getSystemClassLoader(); System.out.println("系统类加载器:" + systemLoader); /* 获取系统类加载器的加载路径--通常CLASSPATH环境变量指定 如果操作系统没有指定ClASSPATH环境变量,则默认以当前路径作为系统类加载器的加载路径 */ Enumeration<URL> em1 = systemLoader.getResources(""); while(em1.hasMoreElements()){ System.out.println(em1.nextElement()); } //获取系统类加载器的父加载器,得到扩展类加载器 ClassLoader extensionLoader = systemLoader.getParent(); System.out.println("扩展类加载器:" + extensionLoader); System.out.println("扩展类加载器的加载路径:" + System.getProperty("java.ext.dirs")); System.out.println("扩展类加载器的parent:" + extensionLoader.getParent()); } }
输出
系统类加载器:[email protected] file:/D:/64liferay/front/javaspace/20160509JavaReflect/bin/ 扩展类加载器:[email protected] 扩展类加载器的加载路径:C:\Program Files\Java\jdk1.7.0_80\jre\lib\ext;C:\Windows\Sun\Java\lib\ext 扩展类加载器的parent:null
2、系统类加载器时AppClassLoader的实例,扩展类加载器是ExtClassLoader的实例。实际上者两个类都是URLClassLoader类的实例。
JVM的根类加载器并不是Java实现的,而且程序无须访问根类加载器,因此访问扩展类加载器的父类加载器时返回null
3、类加载器加载Class大致经过8个步骤
①检查此Class是否载入过(即在缓存区中是否有次class),如果则直接进入第8步,否则接着执行第2步。
② 如果父类加载器不存在(要么没有父加载器,则要么parent一定是根加载器,要么本身就是根加载器),则跳到第4步执行:如果父类加载器存在,则接着执行3步
③请求使用父类加载器去载入目标类,如果成功则跳到8步,否则执行第5步
④请求使用根类加载器来载入目标类,如果成功则跳转第8步,否则调到7步
⑤当前类加载器尝试寻找Class文件(从与此ClassLoader相关的类路径中寻找),如果找到则执行第6步,如果找不到则跳转7步
⑥从文件中载入Class,成功载入跳到8步
⑦抛出ClassNotFoundException异常
⑧返回对应的java.lang.Class对象