JVM 类加载机制(下)

简介: jvm 其实是跨平台和跨语言的。只要是符合 java 虚拟机规范的 bytecode 都能被 jvm 解析并且执行。 以Java 为例子,我们开发的是 .java 文件,然后通过 javac 编译成 .class 文件。 也就是 jvm 能够读取和解析的 bytecode 然后虚拟机将 bytecode 转换为虚拟机指令,然后加载过后给执行引擎去执行。
  1. 类加载 demo 1


public class Test_1 {
    public static void main(String[] args) {
        System.out.printf(Test_1_B.str);
    }
}
class Test_1_A {
    public static String str = "A str";
    static {
        System.out.println("A Static Block");
    }
}
class Test_1_B extends Test_1_A {
    static {
        System.out.println("B Static Block");
    }
}
//输出结果
//A Static Block
//A str


4. 读取静态属性的实现


Java 代码如下:


public class Test_1 {
    public static void main(String[] args) {
        System.out.printf(Test_1_B.str);
    }
}
class Test_1_A {
    public static String str = "A str";
    static {
        System.out.println("A Static Block");
    }
}
class Test_1_B extends Test_1_A {
    static {
        System.out.println("B Static Block");
    }
}


静态字段如何存储, 有两个核心的概念在 JVM 1.8 以前存储在 instanceKlass jdk1.8 是存储在 instanceMirrorKlass 只中的

Test_1_A的


image.png


静态变量str的值存放在StringTable中,镜像类中存放的是字符串的指针


Test_1_B中


image.png


str是类Test_1_A的静态属性,可以看到不会存储到子类Test_1_B的镜像类中 可以猜得到,通过子类Test_1_B访问父类Test_1_A的静态字段有两种实现方式:


1、先去Test_1_B的镜像类中去取,如果有直接返回;如果没有,会沿着继承链将请求往上抛。很明显,这种算法的性能随继承链的death而上升,算法复杂度为O(n)


2、借助另外的数据结构实现,使用K-V的格式存储,查询性能为O(1)


Hotspot就是使用的第二种方式,借助另外的数据结构ConstantPoolCache,常量池类ConstantPool中有个属性_cache指向了这个结构。每一条数据对应一个类ConstantPoolCacheEntry。


ConstantPoolCacheEntry在哪呢?在ConstantPoolCache对象后面,看代码 \openjdk\hotspot\src\share\vm\oops\cpCache.hpp


ConstantPoolCacheEntry* base() const           { 
  return (ConstantPoolCacheEntry*)((address)this + in_bytes(base_offset()));
}


这个公式的意思是ConstantPoolCache对象的地址加上ConstantPoolCache对象的内存大小 ConstantPoolCache 常量池缓存是为常量池预留的运行时数据结构。保存所有字段访问和调用字节码的解释器运行时信息。缓存是在类被积极使用之前创建和初始化的。每个缓存项在解析时被填充 如何读取 \openjdk\hotspot\src\share\vm\interpreter\bytecodeInterpreter.cpp


CASE(_getstatic):
        {
          u2 index;
          ConstantPoolCacheEntry* cache;
          index = Bytes::get_native_u2(pc+1);
          // QQQ Need to make this as inlined as possible. Probably need to
          // split all the bytecode cases out so c++ compiler has a chance
          // for constant prop to fold everything possible away.
          cache = cp->entry_at(index);
          if (!cache->is_resolved((Bytecodes::Code)opcode)) {
            CALL_VM(InterpreterRuntime::resolve_get_put(THREAD, (Bytecodes::Code)opcode),
                    handle_exception);
            cache = cp->entry_at(index);
          }
……


从代码中可以看出,是直接去获取ConstantPoolCacheEntry


5. 常用工具


1. 字节码查看 idea 插件 jclasslib


效果如下:

image.png

2. HSDB JDK 自带插件


  • 使用方式


# jdk 安装目录下执行
# 这里注意需要加一个 sudo 在 macos 上可能存在权限问题
sudo java -cp lib/sa-jdi.jar sun.jvm.hotspot.HSDB


image.png


3. javap 命令

查看字节码和反编译信息


javap -v xxx


4.虚拟机指令查询地址



相关文章
|
7月前
|
安全 Java
对 JVM 的类加载机制以及寻找字节码文件的“双亲委派模型”的理解
对 JVM 的类加载机制以及寻找字节码文件的“双亲委派模型”的理解
43 0
|
7月前
|
安全 前端开发 Java
JDK源码级别彻底剖析JVM类加载机制
JDK源码级别彻底剖析JVM类加载机制
|
2月前
|
存储 Java C语言
【JVM】类加载机制
【JVM】类加载机制
23 1
|
4月前
|
数据库 C# 开发者
WPF开发者必读:揭秘ADO.NET与Entity Framework数据库交互秘籍,轻松实现企业级应用!
【8月更文挑战第31天】在现代软件开发中,WPF 与数据库的交互对于构建企业级应用至关重要。本文介绍了如何利用 ADO.NET 和 Entity Framework 在 WPF 应用中访问和操作数据库。ADO.NET 是 .NET Framework 中用于访问各类数据库(如 SQL Server、MySQL 等)的类库;Entity Framework 则是一种 ORM 框架,支持面向对象的数据操作。文章通过示例展示了如何在 WPF 应用中集成这两种技术,提高开发效率。
63 0
|
4月前
|
安全 前端开发 Java
【JVM 探秘】ClassLoader 类加载器:揭秘 Java 类加载机制背后的秘密武器!
【8月更文挑战第25天】本文全面介绍了Java虚拟机(JVM)中的类加载器,它是JVM的核心组件之一,负责将Java类加载到运行环境中。文章首先概述了类加载器的基本工作原理及其遵循的双亲委派模型,确保了核心类库的安全与稳定。接着详细阐述了启动、扩展和应用三种主要类加载器的层次结构。并通过一个自定义类加载器的例子展示了如何从特定目录加载类。此外,还介绍了类加载器的完整生命周期,包括加载、链接和初始化三个阶段。最后强调了类加载器在版本隔离、安全性和灵活性方面的重要作用。深入理解类加载器对于掌握JVM内部机制至关重要。
169 0
|
7月前
|
前端开发 Java 数据库连接
JVM(类加载机制)
JVM(类加载机制)
61 4
|
7月前
|
存储 缓存 安全
深入浅出JVM(三)之HotSpot虚拟机类加载机制
深入浅出JVM(三)之HotSpot虚拟机类加载机制
|
存储 算法 Java
Android 面试必备 - JVM 及 类加载机制
Android 面试必备 - JVM 及 类加载机制
|
7月前
|
前端开发 Java
深入理解Java虚拟机:类加载机制
【2月更文挑战第23天】本文深入探讨了Java虚拟机(JVM)的类加载机制,包括类加载器的层次结构、类加载的过程以及双亲委派模型。通过对JVM类加载机制的理解,可以帮助我们编写更高效的Java代码。
|
7月前
|
安全 Java 应用服务中间件