JVM类加载

Posted wod-y

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JVM类加载相关的知识,希望对你有一定的参考价值。

JVM class文件格式

  • 魔法数 CAFE BABE
  • 编译器版本号
  • Constant count
  • Constant pool
  • access flag
  • this class
  • super class
  • interface count
  • interfaces
  • field count
  • fields
  • method count
  • methods
  • attribute count
  • attributes

源码:User.java

package com.wod.dao;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Component
@Scope("prototype")
public class User {
  @Value("lisi")
  String username;
  String password;
}

class:User.class

Classfile /d:/work/develop/juc_test/target/classes/com/wod/dao/User.class
  Last modified 2020-6-20; size 562 bytes
  MD5 checksum 27726ee8ded9222615d43ded38db4561
  Compiled from "User.java"
public class com.wod.dao.User
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Class              #2             // com/wod/dao/User
   #2 = Utf8               com/wod/dao/User
   #3 = Class              #4             // java/lang/Object
   #4 = Utf8               java/lang/Object
   #5 = Utf8               username
   #6 = Utf8               Ljava/lang/String;
   #7 = Utf8               RuntimeVisibleAnnotations
   #8 = Utf8               Lorg/springframework/beans/factory/annotation/Value;
   #9 = Utf8               value
  #10 = Utf8               lisi
  #11 = Utf8               password
  #12 = Utf8               <init>
  #13 = Utf8               ()V
  #14 = Utf8               Code
  #15 = Methodref          #3.#16         // java/lang/Object."<init>":()V
  #16 = NameAndType        #12:#13        // "<init>":()V
  #17 = Utf8               LineNumberTable
  #18 = Utf8               LocalVariableTable
  #19 = Utf8               this
  #20 = Utf8               Lcom/wod/dao/User;
  #21 = Utf8               SourceFile
  #22 = Utf8               User.java
  #23 = Utf8               Lorg/springframework/stereotype/Component;
  #24 = Utf8               Lorg/springframework/context/annotation/Scope;
  #25 = Utf8               prototype
{
  java.lang.String username;
    descriptor: Ljava/lang/String;
    flags:
    RuntimeVisibleAnnotations:
      0: #8(#9=s#10)

  java.lang.String password;
    descriptor: Ljava/lang/String;
    flags:

  public com.wod.dao.User();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #15                 // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 16: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/wod/dao/User;
}
SourceFile: "User.java"
RuntimeVisibleAnnotations:
  0: #23()
  1: #24(#9=s#25)

类加载过程

  • loading:class文件通过类加载器加载到内存
  • linking:验证class格式-》静态变量赋默认值-》将类、方法、属性等符号引用(常量池的引用)解析为直接引用
  • initialing:类初始化,静态变量赋初始值

类加载器classloader

双亲委派机制

  • 判断是否已加载:自定义加载器-》AppClassLoader-》ExtClassLoader-》BootStrapClassLoader(native)
  • 依次尝试加载:BootStrapClassLoader-》ExtClassLoader-》AppClassLoader-》自定义加载器

体现双亲委派的代码

protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    c = findClass(name);

                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }

热加载

  • tomcat
  • 双亲委派机制表示加载过就不再加载,所以需要自定义classloader重写loadClass打破双亲委派才能重新加载
public class SuccessedClassReloading2 {
    private static class MyLoader extends ClassLoader {
        @Override
        public Class<?> loadClass(String name) throws ClassNotFoundException {

            File f = new File("D:/work/develop/juc_test/target/classes/" + name.replace(".", "/").concat(".class"));

            if(!f.exists()) return super.loadClass(name);

            try {

                InputStream is = new FileInputStream(f);

                byte[] b = new byte[is.available()];
                is.read(b);
                return defineClass(name, b, 0, b.length);
            } catch (IOException e) {
                e.printStackTrace();
            }

            return super.loadClass(name);
        }
    }

    public static void main(String[] args) throws Exception {
        MyLoader m = new MyLoader();
        Class clazz = m.loadClass("com.wod.dao.User");

        m = new MyLoader();
        Class clazzNew = m.loadClass("com.wod.dao.User");

        //false则表示重新加载成功
        System.out.println(clazz == clazzNew);
    }
}

以上是关于JVM类加载的主要内容,如果未能解决你的问题,请参考以下文章

JVM : 2 JVM类加载机制

JVM类加载

图解JVM类加载机制和双亲委派模型

入门篇JVM类加载机制

JVM类加载机制详解JVM类加载过程

JVM类加载机制详解JVM类加载过程