JVM类加载器及类加载机制详解

我不是女神ヾ 2023-07-08 12:25 139阅读 0赞

类加载器种类

  • 启动类加载器:

    • 负责加载JRE的核心类库,如jre包下的rt.jar、charset.jar等。拓展类加载器:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM4NjMwODEw_size_16_color_FFFFFF_t_70

  • 扩展类加载器:

    • 负责加载jre扩展目录ext中的jar包。

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM4NjMwODEw_size_16_color_FFFFFF_t_70 1

  • 系统类加载器:

    • 用户自定义加载器:
  • 用户自定义加载器:

    • 负责加载用户自定义路径下的类包。

类加载机制

  • 全盘负责委托机制:

    • 一个ClassLoader加载一个类是,除非显示的引用另一个ClassLoader,该类所依赖和引用的类都由该ClassLoader载入。
  • 双亲委派模式:

    • 类加载的时候先委托父类加载器寻找目标类,如果找不到目标类才在的路径中查找并加载目标类。

双亲委派模式如下图:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM4NjMwODEw_size_16_color_FFFFFF_t_70 2

双亲委派模式优点:

  • 沙箱安全机制:防止核心API库被恶意篡改。
  • 避免类重复加载:当父类加载了该类时,子类就不会再次加载该类。

代码演示:

  1. package java.lang;
  2. /**
  3. * @Author xiaolinzi
  4. * @Date 2020/2/26 2:24 下午
  5. * @Email xiaolinzi95_27@163.com
  6. */
  7. public class String {
  8. public static void main(String[] args) {
  9. System.out.println("自定义String类是否可以被加载");
  10. }
  11. }

我们自己写一个java.lang.String类,然后运行其main方法看是否可以加载并运行该类。我们运行自定义String类的main方法。然后报以下错误:

20200226160028479.png

这就意味着我们自己定义的String类并没有被加载,他是使用启动类加载器加载的jdk中rt包下的java.lang.String类,但是该包下的类并没有main方法,所以报错。

那如何证明启动的时候,类加载器加载的是rt包下的java.lang.String而不是我们自定义的String呢?可以在运行代码的时候在vm启动参数加上参数 -verbose:class

如下图:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM4NjMwODEw_size_16_color_FFFFFF_t_70 3

类加载过程:

Jvm对class类文件是按需加载(运行时动态加载),非一次性加载,如以下示例:

代码示例

  1. /**
  2. * @author :zhangshilin
  3. * @date :2020-2-26 15:39
  4. * @Email xiaolinzi95_27@163.com
  5. */
  6. public class ClassLoad {
  7. static {
  8. System.out.println("*************static code************");
  9. }
  10. public static void main(String[] args){
  11. new A();
  12. System.out.println("*************load test************");
  13. new B();
  14. }
  15. }
  16. class A{
  17. public A(){
  18. System.out.println("*************initial A************");
  19. }
  20. }
  21. class B{
  22. public B(){
  23. System.out.println("*************initial B************");
  24. }
  25. }

加上在vm启动参数中加上-verbose:class看控制台日志:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM4NjMwODEw_size_16_color_FFFFFF_t_70 4

可以看出并A,B并不是一开始就加载的,而运行时动态加载。

发表评论

表情:
评论列表 (有 0 条评论,139人围观)

还没有评论,来说两句吧...

相关阅读

    相关 JVM详解

    前言 在上一篇中,通过下面这幅图大致了JVM整体的内部运行结构图,在JVM的结构中,类加载子系统作为连接外部class文件与真正将class文件加载到运行时数据区,承担着

    相关 JVM详解

    首先来了解一下字节码和class文件的区别: 我们知道,新建一个java对象的时候,JVM要将这个对象对应的字节码加载到内存中,这个字节码的原始信息存放在classpat