JVM类加载

这篇具有很好参考价值的文章主要介绍了JVM类加载。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1 类文件结构

执行 javac -parameters -d . HellowWorld.java编译为 HelloWorld.class文件,根据 JVM 规范,类文件结构如下

ClassFile {
u4 magic;	//魔数
u2 minor_version;	//版本
u2 major_version;
u2 constant_pool_count;	//常量池
cp_info constant_pool[constant_pool_count-1];
u2 access_flags;	//访问标识与继承信息
u2 this_class;
u2 super_class;
u2 interfaces_count;
u2 interfaces[interfaces_count];
u2 fields_count;	//Field 信息
field_info fields[fields_count];
u2 methods_count;	//Method 信息
method_info methods[methods_count];
u2 attributes_count;	//附加属性
attribute_info attributes[attributes_count];
}

2 字节码指令

2.1 编译执行流程分析

原始代码如下:

package cn.itcast.jvm.t3.bytecode;
/**
* 演示 字节码指令 和 操作数栈、常量池的关系
*/
public class Demo3_1 {
public static void main(String[] args) {
int a = 10;
int b = Short.MAX_VALUE + 1;
int c = a + b;
System.out.println(c);
}
}

字节码文件自己分析嫌慢,可以执行指令javap -v filepath反编译命令,直接获取字节码指令更直观

[root@localhost ~]# javap -v Demo3_1.class
Classfile /root/Demo3_1.class
Last modified Jul 7, 2019; size 665 bytes
MD5 checksum a2c29a22421e218d4924d31e6990cfc5
Compiled from "Demo3_1.java"
public class cn.itcast.jvm.t3.bytecode.Demo3_1
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #7.#26 // java/lang/Object."<init>":()V
#2 = Class #27 // java/lang/Short
#3 = Integer 32768
#4 = Fieldref #28.#29 //
java/lang/System.out:Ljava/io/PrintStream;
#5 = Methodref #30.#31 // java/io/PrintStream.println:(I)V
#6 = Class #32 // cn/itcast/jvm/t3/bytecode/Demo3_1
#7 = Class #33 // java/lang/Object
#8 = Utf8 <init>
#9 = Utf8 ()V
#10 = Utf8 Code
#11 = Utf8 LineNumberTable
#12 = Utf8 LocalVariableTable
#13 = Utf8 this
#14 = Utf8 Lcn/itcast/jvm/t3/bytecode/Demo3_1;
#15 = Utf8 main
#16 = Utf8 ([Ljava/lang/String;)V
#17 = Utf8 args
#18 = Utf8 [Ljava/lang/String;
#19 = Utf8 a
#20 = Utf8 I
#21 = Utf8 b
#22 = Utf8 c
#23 = Utf8 MethodParameters
#24 = Utf8 SourceFile
#25 = Utf8 Demo3_1.java
#26 = NameAndType #8:#9 // "<init>":()V
#27 = Utf8 java/lang/Short
#28 = Class #34 // java/lang/System
#29 = NameAndType #35:#36 // out:Ljava/io/PrintStream;
#30 = Class #37 // java/io/PrintStream
#31 = NameAndType #38:#39 // println:(I)V
#32 = Utf8 cn/itcast/jvm/t3/bytecode/Demo3_1
#33 = Utf8 java/lang/Object
#34 = Utf8 java/lang/System
#35 = Utf8 out
#36 = Utf8 Ljava/io/PrintStream;
#37 = Utf8 java/io/PrintStream
#38 = Utf8 println
#39 = Utf8 (I)V
{
public cn.itcast.jvm.t3.bytecode.Demo3_1();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."
<init>":()V
4: return
LineNumberTable:
line 6: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcn/itcast/jvm/t3/bytecode/Demo3_1;
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=4, args_size=1
0: bipush 10
2: istore_1
3: ldc #3 // int 32768
5: istore_2
6: iload_1
7: iload_2
8: iadd
9: istore_3
10: getstatic #4 // Field
java/lang/System.out:Ljava/io/PrintStream;
13: iload_3
14: invokevirtual #5 // Method
java/io/PrintStream.println:(I)V
17: return
LineNumberTable:
line 8: 0
line 9: 3
3)常量池载入运行时常量池
4)方法字节码载入方法区
5)main 线程开始运行,分配栈帧内存
(stack=2,locals=4)
line 10: 6
line 11: 10
line 12: 17
LocalVariableTable:
Start Length Slot Name Signature
0 18 0 args [Ljava/lang/String;
3 15 1 a I
6 12 2 b I
10 8 3 c I
MethodParameters:
Name Flags
args
}

问题:方法如何被执行的呢?

ANS:原始代码编译成字节码文件->常量池载入运行时常量池->方法字节码载入方法区->main线程开始运行,分配栈帧内存->执行引擎开始执行字节码->最终在内存结构上表现如下图

JVM类加载,jvm,jvm,java

常用字节码指令参照:

指令码 操作码 描述(栈指操作数栈)
0x03 iconst_0 0(int)值入栈
0x10 bipush valuebyte值带符号扩展成int值入栈
0x11 sipush 将一个 short 值入栈
0x12 ldc 常量池中的常量值入栈
0x2a aload_0 加载 slot 0 的局部变量
0x4b astroe_0 将栈顶值保存到 slot 0 的局部变量中
0x57 pop 从栈顶弹出一个字长的数据。
0x59 dup 复制栈顶一个字长的数据,将复制后的数据压栈。
0x60 iadd 将栈顶两int类型数相加,结果入栈。
0x84 iinc 直接在局部变量 slot 上进行运算
0x9c ifge 若栈顶int类型值大于等于0则跳转。
0xa7 goto 无条件跳转到指定位置。
0xbb new 创建新的对象实例。
0xb4 getfield 获取对象字段的值。
0xb2 getstatic 获取静态字段的值。
0xb7 invokespecial 预备调用构造方法
0xb6 invokevirtual 预备调用成员方法
0xb8 invokestatic 预备调用静态方法
0xb9 invokeinterface 预备调用方法
0xb0 areturn 返回引用类型值。
0xb1 return void函数返回。
0xc2 monitorenter 进入并获得对象监视器。(线程同步)
0xc3 monitorexit 释放并退出对象监视器。(线程同步)

2.2 多态原理

借助工具分析

①jps 获取进程 id

②运行 HSDB 工具,进入 JDK 安装目录,执行java -cp ./lib/sa-jdi.jar sun.jvm.hotspot.HSDB,进入图形界面 attach 进程 id

③查找对象,打开 Tools -> Find Object By Query,输入 select d from cn.itcast.jvm.t3.bytecode.Dog d 点击 Execute 执行

④点击超链接可以看到对象的内存结构,此对象没有任何属性,因此只有对象头的 16 字节,前 8 字节是MarkWord,后 8 字节就是对象的 Class 指针

⑤通过 Windows -> Console 进入命令行模式,执行mem ③中的对象头地址 2

⑥查看类的 vtable,Alt+R 进入 Inspector 工具,输入刚才的⑤得到的 Class 内存地址,得到vtable长度为n

⑦ ⑤得到的 Class 内存地址偏移 0x1b8 就是 vtable 的起始地址,通过 Windows -> Console 进入命令行模式,执行 mem vtable起始地址 6,就得到了 6 个虚方法的入口地址

⑧通过 Tools -> Class Browser 查看每个类的方法定义,比较可知,方法属于那个类,以判断是否多态调用

结论

当执行 invokevirtual 指令时,

  1. 先通过栈帧中的对象引用找到对象

  2. 分析对象头,找到对象的实际 Class

  3. Class 结构中有 vtable,它在类加载的链接阶段就已经根据方法的重写规则生成好了

  4. 查表得到方法的具体地址

  5. 执行方法的字节码

2.3 异常处理

原始代码:

public class Demo3_11_4 {
public static void main(String[] args) {
int i = 0;
try {
i = 10;
} catch (Exception e) {
i = 20;
} finally {
i = 30;
}
}
}

字节码指令:

public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=4, args_size=1
0: iconst_0
1: istore_1 // 0 -> i
2: bipush 10 // try --------------------------------------
4: istore_1 // 10 -> i |
5: bipush 30 // finally |
7: istore_1 // 30 -> i |
8: goto 27 // return -----------------------------------
11: astore_2 // catch Exceptin -> e ----------------------
12: bipush 20 // |
14: istore_1 // 20 -> i |
15: bipush 30 // finally |
17: istore_1 // 30 -> i |
18: goto 27 // return -----------------------------------
21: astore_3 // catch any -> slot 3 ----------------------
22: bipush 30 // finally |
24: istore_1 // 30 -> i |
25: aload_3 // <- slot 3 |
26: athrow // throw ------------------------------------
27: return
Exception table:
from to target type
2 5 11 Class java/lang/Exception
2 5 21 any // 剩余的异常类型,比如 Error
11 15 21 any // 剩余的异常类型,比如 Error
LineNumberTable: ...
LocalVariableTable:
Start Length Slot Name Signature
12 3 2 e Ljava/lang/Exception;
0 28 0 args [Ljava/lang/String;
2 26 1 i I
StackMapTable: ...
MethodParameters: ...

总结:

Exception table 的结构,[from, to) 是前闭后开的检测范围,一旦这个范围内的字节码执行出现异常,则通过 type 匹配异常类型,如果一致,进入 target 所指示行号

11 行的字节码指令 astore_2 是将异常对象引用存入局部变量表的 slot 2 位置

可以看到 finally 中的代码被复制了 3 份,分别放入 try 流程,catch 流程以及 catch 剩余的异常类型流程

2.4 synchronized

原始代码:

public class Demo3_13 {
public static void main(String[] args) {
Object lock = new Object();
synchronized (lock) {
System.out.println("ok");
}
}
}

字节码:

public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=4, args_size=1
0: new #2 // new Object
3: dup
4: invokespecial #1 // invokespecial <init>:()V
7: astore_1 // lock引用 -> lock
8: aload_1 // <- lock (synchronized开始)
9: dup
10: astore_2 // lock引用 -> slot 2
11: monitorenter // monitorenter(lock引用)
12: getstatic #3 // <- System.out
15: ldc #4 // <- "ok"
17: invokevirtual #5 // invokevirtual println:
(Ljava/lang/String;)V
20: aload_2 // <- slot 2(lock引用)
21: monitorexit // monitorexit(lock引用)
22: goto 30
25: astore_3 // any -> slot 3
26: aload_2 // <- slot 2(lock引用)
27: monitorexit // monitorexit(lock引用)
28: aload_3
29: athrow
30: return
Exception table:
from to target type
12 22 25 any
25 28 25 any
LineNumberTable: ...
LocalVariableTable:
Start Length Slot Name Signature
0 31 0 args [Ljava/lang/String;
8 23 1 lock Ljava/lang/Object;
StackMapTable: ...
MethodParameters: ...

总结:

①加载对象然后上锁或者解锁

②异常表的作用是保证上锁的代码块出现异常时,对象锁也能正常释放掉

③方法级别的 synchronized 不会在字节码指令中有所体现

3 编译器处理

语法糖,即.java文件编译为.class字节码文件过程中的代码转换,例如

语法糖 转换
默认构造器 无参构造,方法内调用父类无参构造
自动拆装箱 Integer.valueOf / 整型值x.intValue
泛型集合取值 ((Integer)list.get(0)).intValue()
可变参数 其实是一个数组
foreach 循环 数组转为下标循环,集合则准换为迭代器
switch 字符串 两层switch,第一层先匹配hashcode提高效率, 再匹配内容
switch 枚举 和上面类似,先在静态代码块中将类元素做映射,再两层switch
try-with-resources 接口实现了 AutoCloseable ,使用 try-withresources 可以不用写 finally 语句块,编译器会帮助生成关闭资源代码
方法重写 子类返回值可以是父类返回值的子类,子类中定义了桥接方法
匿名内部类 额外生成类,且如果引用了局部变量,会在新类的有参构造中对该变量赋值

4 类加载阶段

分为三个阶段:加载、链接、初始化

阶段 主要内容
加载 将类的字节码载入方法区中,内部采用 C++ 的 instanceKlass 描述 java 类
重要 field 有_java_mirror 即 java 的类镜像(存储在堆中),例如对 String 来说,就是 String.class,作用是把 klass 暴露给 java 使用
有父类先加载父类
加载和链接可能交替运行
链接 ①验证:验证类是否符合 JVM规范,安全性检查
②准备:为 static 变量分配空间,设置默认值,赋值有两个可能,如果变量为final修饰的基本类型以及字符串常量,准备阶段就能赋值,否则只能初始化再赋值
③解析:将常量池中的符号引用解析为直接引用
初始化 调用 < cinit >()V ,虚拟机会保证这个类的『构造方法』的线程安全

类初始化发生的时机:

会初始化(懒惰的) 不会初始化
main 方法所在的类,总会被首先初始化 访问类的 static final 静态常量(基本类型和字符串)不会触发初始化
首次访问这个类的静态变量或静态方法时 类对象.class 不会触发初始化
子类初始化,如果父类还没初始化,会引发 创建该类的数组不会触发初始化
子类访问父类的静态变量,只会触发父类的初始化 类加载器的 loadClass 方法
Class.forName Class.forName 的参数 2 为 false 时
new 会导致初始化

5 类加载器

名称 加载哪的类 说明
Bootstrap ClassLoader JAVA_HOME/jre/lib 无法直接访问,显示为null
Extension ClassLoader JAVA_HOME/jre/lib/ext 上级为 Bootstrap
Application ClassLoader classpath 上级为 Extension
自定义类加载器 自定义 上级为 Application

一、如何指定类加载器加载指定类?

①启动类加载器:

java -Xbootclasspath/a:. 类路径

其中,可以用下面参数来替换核心类

java -Xbootclasspath: 新路径

java -Xbootclasspath/a: 追加路径(后追加)

java -Xbootclasspath/p: 追加路径(前追加,用于替换核心类)

②扩展类加载器:

jar -cvf my.jar 类路径 打个jar包,拷贝到 JAVA_HOME/jre/lib/ext

二、双亲委派模式怎么理解?

类加载器的 loadClass 方法说明了查找类的规则,如下:

protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException {
    synchronized (getClassLoadingLock(name)) {
        // 1. 检查该类是否已经加载
        Class<?> c = findLoadedClass(name);
        if (c == null) {
            long t0 = System.nanoTime();
            try {
                if (parent != null) {
                    // 2. 有上级的话,委派上级 loadClass
                    c = parent.loadClass(name, false);
                } else {
                    // 3. 如果没有上级了(ExtClassLoader),则委派
                    BootstrapClassLoader
                            c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
            }
            if (c == null) {
                long t1 = System.nanoTime();
                // 4. 每一层找不到,调用 findClass 方法(每个类加载器自己扩展)来加载
                c = findClass(name);
                // 5. 记录耗时
                sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                sun.misc.PerfCounter.getFindClasses().increment();
            }
        }
        if (resolve) {
            resolveClass(c);
        }
        return c;
    }
}

执行流程为:

  1. sun.misc.Launcher$AppClassLoader //1 处, 开始查看已加载的类,结果没有

  2. sun.misc.Launcher A p p C l a s s L o a d e r / / 2 处, = = 委派上级 = = s u n . m i s c . L a u n c h e r AppClassLoader // 2 处,==委派上级==sun.misc.Launcher AppClassLoader//2处,==委派上级==sun.misc.LauncherExtClassLoader.loadClass()

  3. sun.misc.Launcher$ExtClassLoader // 1 处,查看已加载的类,结果没有

  4. sun.misc.Launcher$ExtClassLoader // 3 处,没有上级了,则委派 BootstrapClassLoader 查找

  5. BootstrapClassLoader 是在 JAVA_HOME/jre/lib 下找 H 这个类,显然没有

  6. sun.misc.Launcher E x t C l a s s L o a d e r / / 4 处,调用自己的 f i n d C l a s s 方法,是在 J A V A H O M E / j r e / l i b / e x t 下找 H 这个类,显然没有,回到 s u n . m i s c . L a u n c h e r ExtClassLoader // 4 处,调用自己的 findClass 方法,是在JAVA_HOME/jre/lib/ext 下找 H 这个类,显然没有,回到 sun.misc.Launcher ExtClassLoader//4处,调用自己的findClass方法,是在JAVAHOME/jre/lib/ext下找H这个类,显然没有,回到sun.misc.LauncherAppClassLoader 的 // 2 处

  7. 继续执行到 sun.misc.Launcher$AppClassLoader // 4 处,调用它自己的 findClass 方法,在classpath 下查找,找到了

三、以Driver驱动类为例来分析说明线程上下文类加载器?

我们在使用 JDBC 时,都需要加载 Driver 驱动,不知道你注意到没有,不写

Class.forName("com.mysql.jdbc.Driver")

也是可以让 com.mysql.jdbc.Driver 正确加载的,看源码:

public class DriverManager {
	// 注册驱动的集合
	private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers
		= new CopyOnWriteArrayList<>();
	// 初始化驱动
	static {
		loadInitialDrivers();
		println("JDBC DriverManager initialized");
	}

DriverManager类加载器是 Bootstrap ClassLoader,即该类存在于核心类库, 但 JAVA_HOME/jre/lib 下显然没有 mysql-connector-java-5.1.47.jar 包,这样问题来了,在DriverManager 的静态代码块中,怎么能正确加载 com.mysql.jdbc.Driver 呢?

看 loadInitialDrivers() 方法:

private static void loadInitialDrivers() {
    String drivers;
    try {
        drivers = AccessController.doPrivileged(new PrivilegedAction<String>
                () {
            public String run() {
                return System.getProperty("jdbc.drivers");
            }
        });
    } catch (Exception ex) {
        drivers = null;
    }
    // 1)使用 ServiceLoader 机制加载驱动,即 SPI
    AccessController.doPrivileged(new PrivilegedAction<Void>() {
        public Void run() {
            ServiceLoader<Driver> loadedDrivers =
                    ServiceLoader.load(Driver.class);
            Iterator<Driver> driversIterator = loadedDrivers.iterator();
            try{
                while(driversIterator.hasNext()) {
                    driversIterator.next();
                }
            } catch(Throwable t) {
            // Do nothing
            }
            return null;
        }
    });
    println("DriverManager.initialize: jdbc.drivers = " + drivers);
    // 2)使用 jdbc.drivers 定义的驱动名加载驱动
    if (drivers == null || drivers.equals("")) {
        return;
    }
    String[] driversList = drivers.split(":");
    println("number of Drivers:" + driversList.length);
    for (String aDriver : driversList) {
        try {
            println("DriverManager.Initialize: loading " + aDriver);
            // 这里的 ClassLoader.getSystemClassLoader() 就是应用程序类加载器
            Class.forName(aDriver, true,
                    ClassLoader.getSystemClassLoader());
        } catch (Exception ex) {
            println("DriverManager.Initialize: load failed: " + ex);
        }
    }
}

先看 2)发现它最后是使用 Class.forName 完成类的加载和初始化,关联的是应用程序类加载器,因此可以顺利完成类加载

再看 1)它就是大名鼎鼎的 Service Provider Interface (SPI)约定如下,在 jar 包的 META-INF/services 包下,以接口全限定名名为文件,文件内容是实现类名称

JVM类加载,jvm,jvm,java

再看 1)中ServiceLoader.load 方法可看到底层使用线程上下文类加载器器,默认就是应用程序类加载器,它内部又是由Class.forName 调用了线程上下文类加载器完成类加载

四、自定义类加载器?

步骤:

  1. 继承 ClassLoader 父类

  2. 要遵从双亲委派机制,重写 findClass 方法注意不是重写 loadClass 方法,否则不会走双亲委派机制

  3. 读取类文件的字节码

  4. 调用父类的 defineClass 方法来加载类

  5. 使用者调用该类加载器的 loadClass 方法

示例代码:

class MyClassLoader extends ClassLoader {

    @Override // name 就是类名称
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        String path = "e:\\myclasspath\\" + name + ".class";

        try {
            ByteArrayOutputStream os = new ByteArrayOutputStream();
            Files.copy(Paths.get(path), os);

            // 得到字节数组
            byte[] bytes = os.toByteArray();

            // byte[] -> *.class
            return defineClass(name, bytes, 0, bytes.length);

        } catch (IOException e) {
            e.printStackTrace();
            throw new ClassNotFoundException("类文件未找到", e);
        }
    }
}

6 运行期优化

由JVM内存结构可知 (回顾:JVM内存结构) ,字节码需由解释器逐行解释为机器码再执行,而即时编译器(JIT)不仅能实现这一功能,还能进一步优化,对比如下:

引擎 作用/特点 优势
解释器 将字节码解释为机器码,下次即使遇到相同的字节码,仍会执行重复的解释 将字节码解释为针对所有平台都通用的机器码
即时编译器 根据平台类型,生成平台特定的机器码 将一些字节码编译为机器码,并存入 Code Cache,下次遇到相同的代码,直接执行,无需再编译
缺点显然是耗费时间和资源,因此针对的是热点代码

根据JIT不同参与程度又将JVM执行状态分为5个层次:

0 层,解释执行(Interpreter)

1 层,使用 C1 即时编译器编译执行(不带 profiling)

2 层,使用 C1 即时编译器编译执行(带基本的 profiling)

3 层,使用 C1 即时编译器编译执行(带完全的 profiling)

4 层,使用 C2 即时编译器编译执行

profiling 是指在运行过程中收集一些程序执行状态的数据,例如【方法的调用次数】,【循环的回边次数】等

JIT相关优化的经典应用场景为:文章来源地址https://www.toymoban.com/news/detail-816824.html

应用 说明
逃逸分析 观察新建的对象是否逃逸
方法内联 把热点方法内代码拷贝、粘贴到调用者的位置
常量折叠 9 * 9 替换为81
字段优化 方法外的字段首次读取会缓存起来,以减少访问次数
反射优化 invoke的调用,使用的是MethodAccessor 的 NativeMethodAccessorImpl 实现(本地实现), 当调用次数达到膨胀阈值时,使用 ASM 动态生成的新实现代替本地实现,速度较本地实现快 20 倍左右

到了这里,关于JVM类加载的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Java JVM类加载阶段 双亲委派模式

    加载 将类的字节码载入方法区中,内部采用 C++ 的 instanceKlass 描述 java 类,它的重要 field 有: _java_mirror 即 java 的类镜像,例如对 String 来说,就是 String.class,作用是把 klass 暴露给 java 使用 _super 即父类 _fields 即成员变量 _methods 即方法 _constants 即常量池 _class_loader 即类加载器

    2024年01月25日
    浏览(46)
  • 【Java高级应用:深入探索Java编程的强大功能,JVM 类加载机制, JVM 内存模型,垃圾回收机制,JVM 字节码执行,异常处理机制】

    本人详解 作者:王文峰,参加过 CSDN 2020年度博客之星,《Java王大师王天师》 公众号:JAVA开发王大师,专注于天道酬勤的 Java 开发问题 中国国学、传统文化和代码爱好者的程序人生,期待你的关注和支持!本人外号:神秘小峯 山峯 转载说明:务必注明来源(注明:作者:

    2024年01月16日
    浏览(91)
  • 【Java】JVM执行流程、类加载过程和垃圾回收机制

    JVM,就是Java虚拟机,Java的的程序都是运行在JVM当中。 程序在执行之前先要把java源代码转换成字节码(class文件),JVM 首先需要把字节码通过一定的方(类加载器(ClassLoader)) 把文件加载到内存中的运行时数据区(Runtime Data Area) ,而字节码文件是 JVM 的一套指令集规范,并

    2024年02月16日
    浏览(50)
  • JVM类加载器大比拼:谁才是Java程序的真正主宰?

    主页传送门:📀 传送   JVM(Java虚拟机)的类加载器是Java的核心组件之一,负责将Java字节码文件加载到JVM中,并将其转换为可以执行的Java类。 类加载器的主要职责: 加载类:根据类的全限定名,将类的字节码文件加载到JVM中,并为其创建一个Class对象。 链接类:验证类

    2024年02月08日
    浏览(32)
  • Java安全——JVM类加载器(1),啃下这些Framework技术笔记

    先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7 深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前! 因此收集整理了一份《2024年最新网络安全全套学习资料》

    2024年04月29日
    浏览(37)
  • Java进阶(1)——JVM的内存分配 & 反射Class类的类对象 & 创建对象的几种方式 & 类加载(何时进入内存JVM)& 注解 & 反射+注解的案例

    1.java运行时的内存分配,创建对象时内存分配; 2.类加载的顺序,创建一个唯一的类的类对象; 3.创建对象的方式,new,Class.forName,clone; 4.什么时候加载.class文件进入JVM内存中,看到new,Class.forName; 5.如何加载?双亲委托(委派)机制:安全;AppClassLoader; 6.反射实质:能

    2024年02月14日
    浏览(44)
  • java面经03-虚拟机篇-jvm内存结构&垃圾回收、内存溢出&类加载、引用&悲观锁&HashTable、引用&finalize

    要求 掌握 JVM 内存结构划分 尤其要知道方法区、永久代、元空间的关系 结合一段 java 代码的执行理解内存划分 执行 javac 命令编译源代码为字节码 执行 java 命令 创建 JVM,调用类加载子系统加载 class,将类的信息存入 方法区 创建 main 线程,使用的内存区域是 JVM 虚拟机栈 ,

    2024年02月09日
    浏览(57)
  • 【JVM】JVM类加载机制

    JVM的类加载机制,就是把类,从硬盘加载到内存中 Java程序,最开始是一个Java文件,编译成.class文件,运行Java程序,JVM就会读取.class文件,把文件的内容,放到内存中,并且构造成.class类对象 这里的加载是整个类加载的一个阶段,他和类加载是不同的 在整个类加载的过程中 主要任务就是

    2024年02月07日
    浏览(51)
  • JVM类加载机制-JVM(一)

    我们运行一个.class文件,windows下的java.exe调用底层jvm.dll文件创建java虚拟机(c++实现)。 创建一个引导类加载器实例(c++实现) C++调用java代码Launcher,该类创建其他java类加载器。 Launcher.getClassLoader()调用loaderClass加载运行类Math classLoader.loader(“com.jvm.math”)加载main方法入口

    2024年02月12日
    浏览(46)
  • 【JVM】JVM执行流程 && JVM类加载 && 垃圾回收机制等

    目录 🌷1、JVM是什么? 🌷2、JVM的执行流程(能够描述数据区5部分) 🌷3、JVM类加载过程 🌷4、双亲委派机制:描述类加载的过程 问题1:类加载器 问题2:什么是双亲委派模型?  问题3:双亲委派模型的优点 🌷5、垃圾回收机制(重要,针对的是堆)    问题1:判定对象

    2024年02月15日
    浏览(59)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包