第10讲:深入剖析 Agent 插件原理,无侵入性埋点

这篇具有很好参考价值的文章主要介绍了第10讲:深入剖析 Agent 插件原理,无侵入性埋点。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

AbstractClassEnhancePluginDefine 核心实现

在开始之前,先简单回顾上一课时中关于 AbstractClassEnhancePluginDefine 的一个核心知识点:AbstractClassEnhancePluginDefine 是所有插件的父类,SkywalkingAgent.Transformer 会通过其 enhanceClass() 方法返回的 ClassMatch 对象,匹配到要增强的目标类。在不同的插件实现类中,enhanceClass() 方法返回的 ClassMatch 对象不同,例如:

  • Dubbo 插件拦截的是 com.alibaba.dubbo.monitor.support.MonitorFilter 这个类;
  • Tomcat 插件拦截的是 org.apache.catalina.core.StandardHostValve 这个类。

后面会详细介绍上述两个插件的具体实现。

完成目标类和插件类的匹配之后,会进入 define() 方法,其核心逻辑如下:

  1. 通过 witnessClass() 方法确定当前插件与当前拦截到的目标类的版本是否匹配。若版本不匹配,则 define() 方法直接结束,当前插件类不会增强该类;若版本匹配,则继续后续逻辑。
  2. 进入 enhance() 方法执行增强逻辑。
  3. 设置插件增强标识。

witnessClass() 方法

很多开源组件和工具类库的功能会不断增加,架构也会随之重构,导致不同版本的兼容性得不到很好的保证。例如,MySQL 常用的版本有 5.6、5.7、8.0 多个版本,在使用 JDBC 连接 MySQL 时使用的 mysql-connector-java.jar 包也分为 5.x、6.x、8.x 等版本,对应的 JDBC 协议的版本也各不相同。

SkyWalking Agent 提供的 MySQL 插件本质上是增强 mysql-connector-java.jar 中的关键方法,例如 ConnectionImpl.getInstance() 方法,但在 mysql-connector-java.jar 的 5.x 版本和 8.x 版本中,ConnectionImpl 的包名不同,如下所示:

这仅仅是一个简单的示例,在有的开源组件或类库中,不同版本中同名类的功能和结构已经发生了翻天覆地的变化。要通过一个 SkyWalking Agent 插件完成对一个开源组件所有版本的增强,是非常难实现的,即使勉强能够实现,该插件的实现也会变的非常臃肿,扩展性也会成问题。

SkyWalking 怎么解决这个问题呢?回到 MySQL 示例,SkyWalking 为每个版本的 mysql-connector-java.jar 提供了不同版本的插件,这些插件的 witnessClass() 方法返回值不同,具体返回的是对应版本 mysql-connector-java.jar 所特有的一个类,如下表所示:

若当前类加载器无法扫描到插件 witnessClass() 方法指定的类,表示当前插件版本不合适,即使拦截到了目标类,也不能进行增强。AbstractClassEnhancePluginDefine.define() 方法中的相关片段如下:

String[] witnessClasses = witnessClasses(); 
if (witnessClasses != null) {
    for (String witnessClass : witnessClasses) {
        // 判断指定类加载器中是否存在witnessClasses()指定的类
        if (!WitnessClassFinder.INSTANCE.exist(witnessClass,
               classLoader)) { 
                return null// 若不存在则表示版本不匹配,直接返回
        }
    }
}

增强 static 静态方法

完成上述插件版本的匹配之后,开始进入 enhance() 方法对目标类进行增强。如下图所示, ClassEnhancePluginDefine 继承了 AbstractClassEnhancePluginDefine 抽象类:

在 ClassEnhancePluginDefine 实现的 enhance() 方法中,会分别完成对 static 静态方法以及实例方法的增强:

protected DynamicType.Builder<?> enhance(...) throws PluginException {
    // 增强static方法
    newClassBuilder = this.enhanceClass(typeDescription, 
            newClassBuilder, classLoader); 
    // 增强构造方法和实例方法
    newClassBuilder = this.enhanceInstance(typeDescription, 
            newClassBuilder, classLoader, context); 
    return newClassBuilder;
}

在增强静态方法时会使用到 StaticMethodsInterceptPoint 这个接口,它描述了当前插件要拦截目标类的哪些 static 静态方法,以及委托给哪个类去增强,其定义如下:

public interface StaticMethodsInterceptPoint {
    // 用于匹配目标静态方法
    ElementMatcher<MethodDescription> getMethodsMatcher(); 
    // 拦截到的静态方法交给哪个Interceptor来增强
    String getMethodsInterceptor();
    // 增强过程中是否需要修改参数
    boolean isOverrideArgs();
}

这里以 mysql-8.x-plugin 插件中的实现为例进行说明,其中ConnectionImplCreateInstrumentation 这个插件类的 enhanceClass() 方法如下:

protected ClassMatch enhanceClass() // 拦截目标类为ConnectionImpl
    return byName("com.mysql.cj.jdbc.ConnectionImpl");
}

其 getStaticMethodsInterceptPoints() 方法返回的下面这个 StaticMethodsInterceptPoint 实现(StaticMethodsInterceptPoint 接口的实现基本都是这种匿名内部类):

new StaticMethodsInterceptPoint[] {
    new StaticMethodsInterceptPoint() {
        @Override
        public ElementMatcher<MethodDescription> getMethodsMatcher() {
            return named("getInstance"); // 增强 getInstance()方法
        }

        @Override
        public String getMethodsInterceptor() { 
            // 委托给 ConnectionCreateInterceptor进行增强
            return “org.apache.skywalking.apm.plugin.jdbc
                       .mysql.v8.ConnectionCreateInterceptor”;
        }

        @Override
        public boolean isOverrideArgs() {
            return false; // 增强过程中无需修改方法参数
        }
    }
}

也就是说,ConnectionImplCreateInstrumentation 这个插件拦截的是  com.mysql.jdbc.ConnectionImpl.getInstance() 这个静态方法。

接下来回到 ClassEnhancePluginDefine.enhanceClass() 方法的具体实现:

private DynamicType.Builder<?> enhanceClass(TypeDescription typeDescription,
    DynamicType.Builder<?> newClassBuilder, ClassLoader classLoader) throws PluginException {
    // 获取当前插件的静态方法拦截点,如果该插件不增强静态方法,则该数组为空
    StaticMethodsInterceptPoint[] staticMethodsInterceptPoints = 
          getStaticMethodsInterceptPoints();
    String enhanceOriginClassName = typeDescription.getTypeName();
    for (StaticMethodsInterceptPoint staticMethodsInterceptPoint :
              staticMethodsInterceptPoints) {
        // 进行具体增强的Interceptor名称
        String interceptor = staticMethodsInterceptPoint
              .getMethodsInterceptor();
        // 在增强过程中,是否要修改参数。
        if (staticMethodsInterceptPoint.isOverrideArgs()) {
            // 前面介绍了 Byte Buddy 用法,这里也是一样的,通过method()方法
            // 指定拦截方法的条件
            newClassBuilder = newClassBuilder.method(isStatic()
              .and(staticMethodsInterceptPoint.getMethodsMatcher())) 
                .intercept( 
                    MethodDelegation.withDefaultConfiguration()
                        .withBinders( // 要用Morph注解,需要先绑定
Morph.Binder.install(OverrideCallable.class)
                        // StaticMethodsInterWithOverrideArgs后面展开说
                        ).to(new StaticMethodsInterWithOverrideArgs(interceptor))
                );
        } else { // 下面是不需要修改参数的增强
            newClassBuilder = newClassBuilder.method(isStatic()
             .and(staticMethodsInterceptPoint.getMethodsMatcher()))
                .intercept(MethodDelegation.withDefaultConfiguration()
                .to(new StaticMethodsInter(interceptor))
            );
        }
    }
    return newClassBuilder;
}

根据前文对 Byte Buddy API 的介绍,通过 method() 方法拦截到静态方法之后,如果需要修改方法参数,则会通过 StaticMethodsInterWithOverrideArgs 对象进行增强,其中的 intercept() 方法是其核心实现:

@RuntimeType
public Object intercept(@Origin Class<?> clazz, 
        @AllArguments Object[] allArguments, @Origin Method method,
           @Morph OverrideCallable zuper) throws Throwable {
    // 加载插件指定的StaticMethodsAroundInterceptor
    StaticMethodsAroundInterceptor interceptor = 
       InterceptorInstanceLoader
        .load(staticMethodsAroundInterceptorClassName,
              clazz.getClassLoader());
    MethodInterceptResult result = new MethodInterceptResult();
    // 调用 interceptor.before()做前置处理
    interceptor.beforeMethod(clazz, method, allArguments, 
        method.getParameterTypes(), result);
    Object ret = null;
    try {
        // 根据before()的处理结果判定是否调用目标方法
        if (!result.isContinue()) { 
            ret = result._ret();
        } else {
            // 注意:这里是需要传参的,这些参数我们是可以在before()方法中改动
            // 的,这就是OverrideArgs的意义
            ret = zuper.call(allArguments); 
        }
    } catch (Throwable t) {
        // 如果出现异常,会先通知interceptor中的
        // handleMethodException()方法进行处理
        interceptor.handleMethodException(clazz, method, allArguments, 
            method.getParameterTypes(), t);
        throw t;
    } finally { // 通过after()方法进行后置处理
        ret = interceptor.afterMethod(clazz, method, allArguments,
              method.getParameterTypes(), ret);
    }
    return ret;
}

如果不需要修改方法参数,则会通过 StaticMethodsInter 对象进行增强,其实现与 StaticMethodsInterWithOverrideArgs 类似,唯一区别在于调用目标方法时无法修改参数。

上面使用的 StaticMethodsAroundInterceptor 是个接口,其中定义了如下三个方法:

  • before():在目标方法之前调用。
  • after():在目标方法之后调用。
  • handleMethodException():在目标方法抛出异常时调用。

通过实现 StaticMethodsAroundInterceptor 接口,各个 Agent 插件就可以在静态方法前后添加自定义的逻辑了。

前面提到的 mysql-8.x-plugin 中的 ConnectionImplCreateInstrumentation 自然也实现了该接口。通过对 StaticMethodsInterWithOverrideArgs 以及 StaticMethodsAroundInterceptor 接口的介绍,我们会发现 Agent 插件对静态方法的增强逻辑与 Spring AOP 中环绕通知的逻辑非常类似。

设计模式 TIP
ClassEnhancePluginDefine 是个典型的模板方法模式的使用场景,其 enhanceClass() 方法只实现了增强静态方法的基本流程,真正的增强逻辑全部通过 getStaticMethodsInterceptPoints() 抽象方法推迟到子类实现。在后面增强对象的构造方法和实例方法时,同样会看到类似的实现。

增强实例对象

分析完增强 static 静态方法的相关逻辑之后,我们继续分析增强一个 Java 实例对象的相关逻辑 —— 入口是 enhanceInstance() 方法。enhanceInstance() 方法将分成三个部分来分析其实现:

  • 实现 EnhancedInstance 接口
  • 增强构造方法
  • 增强实例方法
实现 EnhancedInstance 接口

enhanceInstance() 方法首先会为目标类添加了一个字段,同时会让目标类实现 EnhancedInstance 接口,具体实现如下:

// EnhanceContext记录了整个增强过程中的上下文信息,里面就两个boolean值
if (!context.isObjectExtended()) { 
    newClassBuilder = newClassBuilder
        // 定义一个字段private volatile的字段,该字段为Object类型
        .defineField("_$EnhancedClassField_ws", Object.class, 
              ACC_PRIVATE | ACC_VOLATILE)
        // 实现EnhancedInstance接口的方式是读写新
        // 增的"_$EnhancedClassField_ws"字段
        .implement(EnhancedInstance.class) 
        .intercept(FieldAccessor.ofField(CONTEXT_ATTR_NAME));
    context.extendObjectCompleted(); // 标记一下上线文信息
}

EnhancedInstance 接口中定义了 getSkyWalkingDynamicField() 和setSkyWalkingDynamicField() 两个方法,分别读写新增的 _$EnhancedClassField_ws 字段。以前文 demo-webapp 示例中的 HelloWorldController 这个类为例,在 skywalking-agent/debugging/ 目录下可以看到增强后的类如下:

// 实现了EnhancedInstance接口
public class HelloWorldController implements EnhancedInstance {
    private volatile Object _$EnhancedClassField_ws; // 新增字段
    // 对EnhancedInstance的实现
    public Object getSkyWalkingDynamicField() { 
        return this._$EnhancedClassField_ws;
    }

    public void setSkyWalkingDynamicField(Object var1) {
        this._$EnhancedClassField_ws = var1;
    }
    … … // 省略其他业务逻辑相关的方法
}

增强构造方法

接下来,ehanceInstance() 方法会增强实例对象的构造方法,具体流程与增强 static 静态方法的流程类似,唯一区别是这里使用的是 ConstructorInterceptPoint,相关代码片段如下:

ConstructorInterceptPoint[] constructorInterceptPoints = 
   getConstructorsInterceptPoints();
for (ConstructorInterceptPoint constructorInterceptPoint : 
       constructorInterceptPoints) { 
    newClassBuilder = newClassBuilder.constructor(
      constructorInterceptPoint.getConstructorMatcher())
      // 这里对 SuperMethodCall的使用方式和介绍 Byte Buddy基础时说的一毛一样
      .intercept(SuperMethodCall.INSTANCE
          .andThen(MethodDelegation.withDefaultConfiguration() 
             .to(new ConstructorInter(constructorInterceptPoint
                .getConstructorInterceptor(), classLoader))
        )
    );
}

ConstructorInterceptPoint 中描述了插件要增强的构造方法以及增强的 Interceptor 类,与StaticMethodsInterceptPoint 类似,不再展开介绍。

ConstructorInter 与 StaticMethodsInter 类似(这里没有修改构造方法参数的 OverriderArgs 版本,因为此时的构造方法已经调用完成了),ConstructorInter.intercept() 方法的实现如下:

@RuntimeType
public void intercept(@This Object obj, 
        @AllArguments Object[] allArguments) {
    // 前面已经让该对象实现了EnhancedInstance接口,所以这里的类型转换是安全的
    EnhancedInstance targetObject = (EnhancedInstance)obj;
    interceptor.onConstruct(targetObject, allArguments);
}

这里使用的 InstanceConstructorInterceptor 接口与前文介绍的 StaticMethodsAroundInterceptor 接口作用相同,都是留给各个插件去实现增强逻辑的。InstanceConstructorInterceptor 接口的定义如下:

public interface InstanceConstructorInterceptor {
    void onConstruct(EnhancedInstance objInst, Object[] allArguments);
}
mysql-8.x-plugin 插件对 ConnectionImpl 的增强

到这里你可能感觉实现逻辑有点乱,这里我将以 mysql-8.x-plugin 插件为例,把静态方法增强、构造方法增强等逻辑串起来。

首先来看 mysql-connector-java-8.x.jar 中  com.mysql.cj.jdbc.ConnectionImpl.getInstance() 方法,这是我们创建数据连接的最常用方法,具体实现:

public static JdbcConnection getInstance(HostInfo hostInfo)
       throws SQLException {
    return new ConnectionImpl(hostInfo);  // 创建 ConnectionImpl实例
}

先来看 mysql-8.x-plugin 模块的 skywalking-plugin.def 文件,其中定义了ConnectionInstrumentation 这个插件类,它会被 AgentClassLoader 加载,其 enhanceClass() 方法返回的 Matcher 拦截的目标类是 com.mysql.cj.jdbc.ConnectionImpl。

虽然 ConnectionInstrumentation 并不拦截构造方法(因为它的 getConstructorsInterceptPoints() 方法返回的是空数组),但是依然会修改 ConnectionImpl,为其添加 _$EnhancedClassField_ws 字段并实现 EnhanceInstance接口。

在 skywalking-plugin.def 文件中还定义了 ConnectionImplCreateInstrumentation 这个插件类,正如前面介绍的那样,它会拦截 com.mysql.cj.jdbc.ConnectionImpl 的 getInstance() 方法,并委托给 ConnectionCreateInterceptor 进行增强。ConnectionCreateInterceptor 中的 before() 和 handleMethodException() 方法都是空实现,其 after() 方法会记录新建 Connection 的一些信息,具体实现如下:

public Object afterMethod(Class clazz, Method method, 
      Object[] allArguments, Class<?>[] parameterTypes, Object ret) {
    if (ret instanceof EnhancedInstance) { // ConnectionImpl已经被增强了
        // ConnectionInfo中记录了DB名称、DB类型以及地址等等信息,具体构造过程省
        // 略,它会被记录到前面新增的 _$EnhancedClassField_ws 那个字段中
        ConnectionInfo connectionInfo = ...
        ((EnhancedInstance) ret).setSkyWalkingDynamicField(
            connectionInfo);
    }
    return ret;
}

另外,这里还会看到一个 AbstractMysqlInstrumentation 抽象类,继承关系如下图所示:

AbstractMysqlInstrumentation 实现了 witnessClasses() 方法以及 ClassEnhancePluginDefine 中的三个 get*InterceptPoints() 抽象方法(这三个方法都返回 null),其中 witnessClasses() 方法返回"com.mysql.cj.interceptors.QueryInterceptor"字符串,witnessClasses() 方法作用不再重复。

AbstractMysqlInstrumentation 的子类只需根据需求实现相应的 get*InterceptPoints() 方法即可,无需再提供其他剩余 get*InterceptPoints() 方法的空实现。在其他版本的 MySQL 插件中也有 AbstractMysqlInstrumentation 这个抽象类,功能相同,不再重复。

增强实例方法

最后,我们来看 enhanceInstance() 方法对实例方法的增强,其实和增强静态方法的套路一样,我们直接看代码吧:

InstanceMethodsInterceptPoint[] instanceMethodsInterceptPoints = 
    getInstanceMethodsInterceptPoints();
for (InstanceMethodsInterceptPoint instanceMethodsInterceptPoint : 
           instanceMethodsInterceptPoints) {
    String interceptor = instanceMethodsInterceptPoint
         .getMethodsInterceptor();
    // 目标方法的匹配条件
    ElementMatcher.Junction<MethodDescription> junction =
       not(isStatic()).and(instanceMethodsInterceptPoint
         .getMethodsMatcher()); 
    if (instanceMethodsInterceptPoint instanceof 
            DeclaredInstanceMethodsInterceptPoint) {
        // 目标方法必须定义在目标类中
        junction = junction.and(ElementMatchers.
             <MethodDescription>isDeclaredBy(typeDescription)); 
    }
    if (instanceMethodsInterceptPoint.isOverrideArgs()){ //修改方法参数
        newClassBuilder = newClassBuilder
           .method(junction) // 匹配目标方法
           .intercept(MethodDelegation.withDefaultConfiguration()
            // 使用@Morph注解之前,需要通过Morph.Binder绑定一下
            .withBinders(Morph.Binder.install(OverrideCallable.class))
            .to(new InstMethodsInterWithOverrideArgs(interceptor, 
                classLoader)));
    } else {
        // ...省略不需要重载参数的部分...
    }
}

增强实例方法过程中使用到的类,在增强静态方法中都有对应的类,如下表所示:

这些类的具体功能不再展开介绍了。

最后依然以 mysql-8.x-plugin 插件为例介绍一下它对实例方法的增强过程,其中  ConnectionInstrumentation.getInstanceMethodsInterceptPoints() 方法返回了 5 个 InstanceMethodsInterceptPoint 对象,这里只看其中的第一个对象:它负责拦截  ConnectionImpl 的 prepareStatement() 方法,并委托给 CreatePreparedStatementInterceptor(不修改方法参数),具体实现代码就不展示了。

在 CreatePreparedStatementInterceptor 中,before() 和 handleMethodException() 方法都是空实现,其 after() 方法实现如下:

public Object afterMethod(EnhancedInstance objInst, Method method, 
    Object[] allArguments, Class<?>[] argumentsTypes,
    Object ret) throws Throwable {
    if (ret instanceof EnhancedInstance) { // ConnectionImpl已被增强过
        // 更新_$EnhancedClassField_ws字段,StatementEnhanceInfos中不仅封
        // 装了原有的ConnectionInfo,还包含了具体执行的SQL语句和SQL参数等信息
        ((EnhancedInstance)ret).setSkyWalkingDynamicField(
          new StatementEnhanceInfos(
             (ConnectionInfo)objInst.getSkyWalkingDynamicField(),
                 (String)allArguments[0], "PreparedStatement"));
    }
    return ret;
}

InterceptorInstanceLoader

前面加载 Interceptpr 的 ClassLoader 并没有使用 AgentClassLoader 的默认实例或是Application ClassLoader,而是通过 InterceptorInstanceLoader 完成加载的。 在 InterceptorInstanceLoader 里面会维护一个 ClassLoader Cache,以及一个 Instance Cache,如下所示:

// 记录了 instanceKey与实例之间的映射关系,保证单例
static ConcurrentHashMap<String, Object> INSTANCE_CACHE = 
    new ConcurrentHashMap<String, Object>();

// 记录了 targetClassLoader以及其子 AgentClassLoader的对应关系
static Map<ClassLoader, ClassLoader> EXTEND_PLUGIN_CLASSLOADERS = 
     new HashMap<ClassLoader, ClassLoader>();

在通过 InterceptorInstanceLoader.load() 这个静态方法加载 Interceptor 类时的核心逻辑如下:

public static <T> load(String className, 
         ClassLoader targetClassLoader){
    if (targetClassLoader == null) {
          targetClassLoader = 
               InterceptorInstanceLoader.class.getClassLoader();
    }
    // 通过该 instanceKey保证该 Interceptor在一个 ClassLoader中只创建一次
    String instanceKey = className + "_OF_" + 
        targetClassLoader.getClass().getName() + "@" + 
           Integer.toHexString(targetClassLoader.hashCode());
    Object inst = INSTANCE_CACHE.get(instanceKey);
    if (inst == null) {
        // 查找targetClassLoader对应的子AgentClassLoader
        ClassLoader pluginLoader = 
                EXTEND_PLUGIN_CLASSLOADERS.get(targetClassLoader);
        if (pluginLoader == null) {
            // 为 targetClassLoader创建子AgentClassLoader
            pluginLoader = new AgentClassLoader(targetClassLoader);
            EXTEND_PLUGIN_CLASSLOADERS.put(targetClassLoader, 
              pluginLoader);
        }
        // 通过子AgentClassLoader加载Interceptor类
        inst = Class.forName(className, true, 
               pluginLoader).newInstance();
        if (inst != null) { // 记录Interceptor对象
            INSTANCE_CACHE.put(instanceKey, inst);
        }
    }
    return (T) inst;
}

以 demo-webapp 为例,其类加载器的结构如下图所示:

总结

Agent 插件增强目标类的实现,这是 Agent 最核心功能,其中深入分析了增强静态方法、构造方法、实例方法的原理,以及插件如何让目标实例对象实现 EnhanceInstance 接口,如何为目标实例对象添加新字段等。为了帮助你更好的理解,在分析的过程中还以 mysql-8.x-plugin 插件为例将上述核心逻辑串连起来。文章来源地址https://www.toymoban.com/news/detail-478360.html


到了这里,关于第10讲:深入剖析 Agent 插件原理,无侵入性埋点的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • 深入剖析 Qt QMultiMap :原理、应用与技巧

    随着软件开发的不断演进,数据结构在程序设计中扮演着至关重要的角色。它们为开发人员提供了丰富的工具来有效地管理和操纵数据。在这个博客中,我们将重点关注一种特殊的数据结构——QMultiMap,这是Qt框架中提供的一个强大且实用的容器类。在接下来的文章中,我们

    2023年04月21日
    浏览(26)
  • 深入剖析mmap原理 - 从三个关键问题说起

    作者:招财二师兄 链接:https://www.jianshu.com/p/eece39beee20 来源:简书 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 对于mmap,您是否能从原理上解析以下三个问题: 1:mmap比物理内存+swap空间大情况下,是否有问题? 2:MAP_SHARED,MAP_PRIVATE,MAP_

    2024年02月09日
    浏览(34)
  • 深入剖析 Qt QMap:原理、应用与技巧

    QMap是Qt框架中的一个关联容器类,用于存储键值对。它提供了高效且易于使用的方法来处理键值对数据,使得开发者可以在各种实际场景中轻松地存储和检索数据。QMap内部使用平衡二叉树(红黑树)作为底层数据结构,提供了高效的插入、删除和查找操作。掌握QMap及其基本

    2023年04月21日
    浏览(38)
  • 深入理解 python 虚拟机:字节码教程(3)——深入剖析循环实现原理

    在本篇文章当中主要给大家介绍 cpython 当中跟循环相关的字节码,这部分字节码相比起其他字节码来说相对复杂一点,通过分析这部分字节码我们对程序的执行过程将会有更加深刻的理解。 我们使用各种例子来理解和循环相关的字节码: 上面的代码对应的字节码如下所示:

    2023年04月15日
    浏览(28)
  • 深入剖析ThreadLocal使用场景、实现原理、设计思想

    ThreadLocal可以用来存储线程的本地数据,做到线程数据的隔离 ThreadLocal的使用不当可能会导致内存泄漏,排查内存泄漏的问题,不仅需要熟悉JVM、利用好各种分析工具还耗费人工 如果能明白其原理并正确使用,就不会导致各种意外发生 本文将从使用场景、实现原理、内存泄漏

    2024年02月08日
    浏览(52)
  • Innodb底层原理与Mysql日志机制深入剖析

    大体来说,MySQL 可以分为 Server 层和存储引擎层两部分。 主要包括连接器、查询缓存、分析器、优化器、执行器等,涵盖 MySQL 的大多数核心服务功能,以及所有的内置函数(如日期、时间、数学和加密函数等),所有跨存储引擎的功能都在这一层实现,比如存储过程、触发器

    2024年02月21日
    浏览(29)
  • 数据结构 | 后缀表达式【深入剖析堆栈原理】

    Hello,大家好,国庆的第二天,带来的是数据结构中堆栈部分的后缀表达式,这也是一块有关栈的应用方面困扰了众多同学的一个大难题,今天就让我们一起解决这个难题📕 后缀表达式也称为 逆波兰表达式 ,也就是将算术运算符放在操作数的后面 例如【1 + 2 * 3】的后缀表达

    2023年04月27日
    浏览(37)
  • “深入剖析JVM内部原理:解密Java虚拟机的奥秘“

    标题:深入剖析JVM内部原理:解密Java虚拟机的奥秘 摘要:本文将深入探讨Java虚拟机(JVM)的内部原理,包括其架构、内存管理、垃圾回收机制、即时编译器等关键组成部分。通过解密JVM的奥秘,我们将更好地理解Java程序的执行过程,并能够优化代码的性能。 正文: 一、

    2024年02月13日
    浏览(34)
  • “深入剖析JVM内部机制:探索Java虚拟机的运行原理“

    标题:深入剖析JVM内部机制:探索Java虚拟机的运行原理 摘要:本文将深入探讨Java虚拟机(JVM)的内部机制,包括类加载、内存管理、垃圾回收、即时编译等关键概念和原理,帮助开发者更好地理解JVM的运行机制。 正文: 一、类加载机制 Java虚拟机通过类加载机制将字节码文

    2024年02月14日
    浏览(45)
  • “深入剖析JVM内部机制:了解Java虚拟机的工作原理“

    标题:深入剖析JVM内部机制:了解Java虚拟机的工作原理 摘要:本文将深入剖析JVM内部机制,详细介绍Java虚拟机的工作原理。我们将探讨JVM的组成部分、类加载过程、内存管理、垃圾回收以及即时编译等关键概念。此外,还将提供示例代码来帮助读者更好地理解JVM的内部机制

    2024年02月11日
    浏览(32)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包