对象的访问定位及类加载机制

一、对象的访问定位

Object objectRef = new Object();  

Object objectRef 这部分将会反应到Java栈的本地变量表中,作为一个reference类型数据出现。而new Object()这部分将会反应到Java堆中,形成一块存储Object类型所有实例数据值的结构化内存,根据具体类型以及虚拟机实现的对象内存布局的不同,这块内存的长度是不固定的。

有两种基本的定位方式:

句柄访问(间接):Java堆中划分一块内存作为句柄池,reference中存储的就是对象在句柄池中的地址,得到了句柄池的地址就可以知道对象的实例数据和类型数据的位置。


直接指针访问(直接):reference中存储的直接就是对象的实例数据的地址,而实例数据中自己有一个指针存储对象类型数据的地址(方法区中),不需要reference来存储。


使用句柄访问方式的最大好处就是reference中存储的是稳定的句柄地址,在对象被移动时只会改变句柄中的实例数据指针,reference本身不会被修改。使用直接指针访问方式好处就是速度更快,节省了一次指针定位的时间开销。

二、类加载机制

      类在运行期间动态加载的。

   类的生命周期

    JVM类加载机制分为五个部分:加载,验证,准备,解析,初始化,如下图所示。


类加载过程(第一次使用该类时加载)

用户自定义的类装载器以及class类的实例都放在内存中的堆区,装载的类型信息都位于方法区。

加载:查找并装载类型的二进制信息。

验证:确保被导入类型的正确性。

            文件格式验证,元数据验证,字节码验证,符号引用验证。

准备:类变量是被static修饰的变量,准备阶段为类变量分配内存并设置其初始值(默认值),使用的是方法区的内存。实例变               量不会在这个阶段分配内存,它将会在对象实例化时随着对象一起分配在Java堆中。

解析:常量池中的符号引用转为内存直接引用(句柄、偏移量)。

初始化:除了在加载阶段可以自定义类加载器以外,其它操作都由JVM主导。到了初始化阶段,才开始真正执行类中定义的Java                   程序代码。初始化阶段即虚拟机执行类构造器<clinit>()方法的过程。对类变量进行赋值和static语句块进行合并。

静态语句块只能访问到定义在它之前的类变量,定义在它之后的类变量只能赋值,不能访问。

三、类加载器

启动类加载器(Bootstrap ClassLoader):负责加载Java的核心类,并不是Java.lang.ClassLoader的子类,用c++实现,由JVM自身实现。

扩展类加载器(Extension ClassLoader):负责加载JRE的扩展目录。

应用程序类加载器(Application ClassLoader):负责将系统类路径(ClassPath)中指定的类加载到内存中。

程序可以通过ClassLoader的静态方法getSystemClassLoader()来获取系统类加载器。

双亲委派模型:用户自定义的类加载器,它们的层次关系除了顶层的启动类加载器,其余的类加载器都应该有自己的父类加载器。类加载器之间的父子关系是组合关系来实现,类加载实例之间的关系,而不是通过继承的关系来实现。

双亲委派模型的实现:java.lang.classloader的loadClass()方法中,先检查是否已经被加载过,若没有加载则调用父类加载器的loadClass()方法,若父类加载器为空,则默认使用启动类加载器作为父类加载器,如果父类加载失败,则抛出ClassNotFoundException异常后,再调用自己的findClass()方法进行加载,(全在LoadClass中实现)。

protected synchronized Class<?> loadClass(String name, boolean resolve)
         throws ClassNotFoundException {
     // First, check if the class has already been loaded
     Class c = findLoadedClass(name);
     if (c == null ) {
         try {
             if (parent != null ) {
                 c = parent.loadClass(name, false );
             } else {
                 c = findBootstrapClassOrNull(name);
             }
         } catch (ClassNotFoundException e) {
             // If still not found, then invoke findClass in order
             // to find the class.
             c = findClass(name);
         }
     }
     if (resolve) {
         resolveClass(c);
     }
     return c;
}

首先通过Class c =findLoadedClass(name);判断一个类是否已经被加载过。

如果没有被加载过执行if(c == null)中的程序,遵循双亲委派模型,首先会通过递归从父类加载器开始找,直到父类加载器是Bootstrap ClassLoader为止。

最后,根据resolve的值,判断这个class是否需要解析。

猜你喜欢

转载自blog.csdn.net/peixie9441/article/details/80590120
今日推荐