我为什么不建议使用框架默认的 DefaultMeterObservationHandler

news2024/9/21 0:43:14

我为什么不建议使用框架默认的 DefaultMeterObservationHandler

个人创作公约:本人声明创作的所有文章皆为自己原创,如果有参考任何文章的地方,会标注出来,如果有疏漏,欢迎大家批判。如果大家发现网上有抄袭本文章的,欢迎举报,并且积极向这个 github 仓库 提交 issue,谢谢支持~
另外,本文为了避免抄袭,会在不影响阅读的情况下,在文章的随机位置放入对于抄袭和洗稿的人的“亲切”的问候。如果是正常读者看到,笔者在这里说声对不起,。如果被抄袭狗或者洗稿狗看到了,希望你能够好好反思,不要再抄袭了,谢谢。

背景知识

最近,我们升级了 SpringBoot 3.x,并且,升级后,我们全面改造了原来的 Sleuth 以及 Micrometer 监控,最新的 io.micrometer.observation.Observation 抽象将链路追踪以及指标监控(opentracing 和 opentelemetry 两个标准)结合,这样,我们就可以在链路追踪中,同时记录指标监控数据了。

并且,在此基础上,我们还加入了全局的 io.micrometer.observation.ObservationHandler,用于在 Observation start 的时候,生成 JFR 事件,在 stop 的时候 commit JFR 事件。这样我们就实现了通过一个 Observation

    1. 暴露指标监控数据到 /actuator/prometheus,供 Prometheus 采集,Grafana 展示:img.png
    1. 上报链路追踪数据到 Jaeger:img.png
    1. 生成 JFR 事件,供 JDK Mission Control 分析:img.png

为何要这么做呢?

  • 指标数据是统计数据,是聚合数据,是一段时间内的数据,而链路追踪数据是实时数据,是每个请求的数据
  • 但是像是链路追踪上报在生产上肯定不能 100% 上报(上报性能,还有成本考虑,以及查询的存储性能有限,成本不能太高),而是采样上报,这样就会导致链路追数据不全
  • JFR 是保留详细数据的好选择,保留在本地,只有发生问题需要排查的时候,事后按需获取就行。

这样,既节约了成本,又能及时报警,又能容易通过 Jaeger 通过链路追踪数据定位出问题的实例,然后通过获取对应实例的 JFR 事件定位详细问题。

全面使用 Observation 遇到了内存溢出以及 CPU 飙高(非不断 GC 导致)

但是,我们在全面使用 Observation 的时候,发现了一个问题,就是内存溢出以及 CPU 飙高(非不断 GC 导致),刚开始我们因为 CPU 飙高是内存溢出引起的,但是后来发现,并不只这个原因。

首先为何会出现内存溢出,我们先做个测试,添加依赖:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.1</version>
    </parent>

    <groupId>com.github.hashjang</groupId>
    <artifactId>wwsmbjysymrdo</artifactId>

    <properties>
        <!--所有项目 Java 基线版本为 17,如果需要升级,修改这里,注意不可降级-->
        <!--Baseline Java Version 17, if you need to upgrade, modify here, note that it cannot be downgraded-->
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

        <!--这里放我们自定义的依赖版本属性-->
        <disruptor.version>3.4.4</disruptor.version>
    </properties>

    <dependencies>
        <!--lombok简化代码-->
        <!--lombok simplifies code-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <!--日志需要用log4j2-->
        <!--choose log4j2 for logging-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j2</artifactId>
        </dependency>
        <!--log4j2异步日志需要的依赖,所有项目都必须用log4j2和异步日志配置-->
        <!--Dependencies required for log4j2 asynchronous logging, all projects must use log4j2 and asynchronous logging configuration-->
        <dependency>
            <groupId>com.lmax</groupId>
            <artifactId>disruptor</artifactId>
            <version>${disruptor.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
    </dependencies>
</project>

之后编写代码测试:

package com.github.hashjang.wwsmbjysymrdo;

import io.micrometer.common.KeyValue;
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Tag;
import io.micrometer.core.instrument.Timer;
import io.micrometer.core.instrument.observation.MeterObservationHandler;
import io.micrometer.observation.Observation;
import io.micrometer.observation.ObservationRegistry;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;

@Log4j2
@SpringBootApplication
public class Main {
    public static void main(String[] args) {
        SpringApplication.run(Main.class);
    }

    @Component
    public static class Runner implements CommandLineRunner {
        @Autowired
        private ObservationRegistry observationRegistry;

        @Override
        public void run(String... args) throws Exception {
            //不断创建新的 Observation,然后 start
            while(true) {
                try {
                    Observation test = Observation.createNotStarted("test", observationRegistry);
                    test.start();
                } catch (Throwable r) {
                    log.error("error", r);
                    break;
                }
            }
            log.info("complete");
        }
    }
}

之后,限制 JVM 最大堆内存为 32m 之后启动,可以看到,不断创建新的 Observation,然后 start,最终导致内存溢出:


  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v3.2.1)

2024-01-19T12:50:37.474+08:00  INFO 21401 --- [           main] c.g.h.w.Main                             : Starting Main using Java 17.0.8 with PID 21401 (/Users/hash/Desktop/Project/Java/spring-cloud-native/article/Problem/wwsmbjysymrdo/target/classes started by hash in /Users/hash/Desktop/Project/Java/spring-cloud-native)
2024-01-19T12:50:37.475+08:00  INFO 21401 --- [           main] c.g.h.w.Main                             : No active profile set, falling back to 1 default profile: "default"
2024-01-19T12:50:37.853+08:00  INFO 21401 --- [           main] c.g.h.w.Main                             : Started Main in 0.529 seconds (process running for 0.762)
Exception in thread "RMI TCP Connection(idle)" java.lang.OutOfMemoryError: Java heap space
WARN StatusConsoleListener org.apache.logging.log4j.spi.AbstractLogger caught java.lang.OutOfMemoryError logging ReusableSimpleMessage: error
 java.lang.OutOfMemoryError: Java heap space
2024-01-19T12:50:40.437+08:00  INFO 21401 --- [           main] c.g.h.w.Main                             : complete
2024-01-19T12:50:40.913+08:00  WARN 21401 --- [ionShutdownHook] o.s.b.f.s.DisposableBeanAdapter          : Invocation of close method failed on bean with name 'log4j2Metrics': java.lang.OutOfMemoryError: Java heap space

这里发生了内存溢出,但是仅仅因为 Observation 的创建和 start,就导致内存溢出,这是不合理的,我们分析一下,为何会出现这个问题。

为何会出现内存溢出

我们通过增加如下启动参数启动并且在退出的时候 dump JFR:

-XX:StartFlightRecording=disk=true,dumponexit=true

或者使用下面的参数在内存溢出的时候 dump 整个堆:

-XX:+HeapDumpOnOutOfMemoryError

都可以看到,内存溢出的时候,堆中有大量的 io.micrometer.core.instrument.internal.DefaultLongTaskTimer$SampleImpl 对象:
img.png

查看源码,发现在 org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration 中,会自动配置一些全局的 ObservationHandler

https://github.com/spring-projects/spring-boot/blob/main/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/observation/ObservationAutoConfiguration.java

	@Configuration(proxyBeanMethods = false)
	@ConditionalOnClass(MeterRegistry.class)
	@ConditionalOnMissingClass("io.micrometer.tracing.Tracer")
	static class OnlyMetricsConfiguration {

		@Bean
		ObservationHandlerGrouping metricsObservationHandlerGrouping() {
			return new ObservationHandlerGrouping(MeterObservationHandler.class);
		}

	}

	@Configuration(proxyBeanMethods = false)
	@ConditionalOnClass(Tracer.class)
	@ConditionalOnMissingClass("io.micrometer.core.instrument.MeterRegistry")
	static class OnlyTracingConfiguration {

		@Bean
		ObservationHandlerGrouping tracingObservationHandlerGrouping() {
			return new ObservationHandlerGrouping(TracingObservationHandler.class);
		}

	}

	@Configuration(proxyBeanMethods = false)
	@ConditionalOnClass({ MeterRegistry.class, Tracer.class })
	static class MetricsWithTracingConfiguration {

		@Bean
		ObservationHandlerGrouping metricsAndTracingObservationHandlerGrouping() {
			return new ObservationHandlerGrouping(
					List.of(TracingObservationHandler.class, MeterObservationHandler.class));
		}

	}

	@Configuration(proxyBeanMethods = false)
	@ConditionalOnBean(MeterRegistry.class)
	@ConditionalOnMissingBean(MeterObservationHandler.class)
	static class MeterObservationHandlerConfiguration {

		@ConditionalOnMissingBean(type = "io.micrometer.tracing.Tracer")
		@Configuration(proxyBeanMethods = false)
		static class OnlyMetricsMeterObservationHandlerConfiguration {

			@Bean
			DefaultMeterObservationHandler defaultMeterObservationHandler(MeterRegistry meterRegistry) {
				return new DefaultMeterObservationHandler(meterRegistry);
			}

		}

		@ConditionalOnBean(Tracer.class)
		@Configuration(proxyBeanMethods = false)
		static class TracingAndMetricsObservationHandlerConfiguration {

			@Bean
			TracingAwareMeterObservationHandler<Observation.Context> tracingAwareMeterObservationHandler(
					MeterRegistry meterRegistry, Tracer tracer) {
				DefaultMeterObservationHandler delegate = new DefaultMeterObservationHandler(meterRegistry);
				return new TracingAwareMeterObservationHandler<>(delegate, tracer);
			}

		}

	}

	@Configuration(proxyBeanMethods = false)
	@ConditionalOnClass(Advice.class)
	static class ObservedAspectConfiguration {

		@Bean
		@ConditionalOnMissingBean
		ObservedAspect observedAspect(ObservationRegistry observationRegistry) {
			return new ObservedAspect(observationRegistry);
		}

	}

以上代码的意思是,根据你的项目中是否添加了链路追踪,或者指标监控的依赖,来初始化不同的 ObservationHandler,如果你的项目中只有指标监控,那么就会初始化 DefaultMeterObservationHandler,这个 DefaultMeterObservationHandler 会在每个 Observationstart 的时候,创建一个 io.micrometer.core.instrument.internal.DefaultLongTaskTimer$SampleImpl 对象,然后将这个 io.micrometer.core.instrument.internal.DefaultLongTaskTimer$SampleImpl 对象放入 DefaultLongTaskTimeractiveTasks 中,这是一个 ConcurrentLinkedDeque。在调用 Observationstop 的时候,会从 DefaultLongTaskTimeractiveTasks 中移除这个 io.micrometer.core.instrument.internal.DefaultLongTaskTimer$SampleImpl 对象。如果没有 stop,那么这个 ConcurrentLinkedDeque 就会越来越大,最终导致内存溢出。

为何解决内存溢出之后,还会出现 CPU 飙高

知道问题之后,我们给遗漏 stop 的地方加上了 try finally stop。但是,我们发现,即使加上了 try finally stop,也会出现 CPU 飙高的问题,我们通过 JFR 看一下,CPU 究竟消耗在哪里:

img.png

我们惊奇的发现,还是和 DefaultLongTaskTimer 有关,看来,即使我们加上了 try finally stop,但是 DefaultLongTaskTimeractiveTasks 里面还是有很多 io.micrometer.core.instrument.internal.DefaultLongTaskTimer$SampleImpl 对象,导致多线程 stop 的时候 CPU 飙高。我们做个实验:

package com.github.hashjang.wwsmbjysymrdo;

import io.micrometer.common.KeyValue;
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Tag;
import io.micrometer.core.instrument.Timer;
import io.micrometer.core.instrument.observation.MeterObservationHandler;
import io.micrometer.observation.Observation;
import io.micrometer.observation.ObservationRegistry;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;

@Log4j2
@SpringBootApplication
public class Main {
    public static void main(String[] args) {
        SpringApplication.run(Main.class);
    }

    @Component
    public static class Runner implements CommandLineRunner {
        @Autowired
        private ObservationRegistry observationRegistry;

        @Override
        public void run(String... args) throws Exception {
            ExecutorService executor = Executors.newFixedThreadPool(100);
            //预热
            for (int j = 0; j < 100000; j++) {
                executor.execute(() -> {
                    Observation observation = Observation.createNotStarted("test", observationRegistry);
                    observation.start();
                    observation.stop();
                });
            }
            //测试
            long start = System.currentTimeMillis();
            Future<?> test[] = new Future[100];
            for (int i = 0; i < 100; i++) {
                test[i] = executor.submit(() -> {
                    for (int j = 0; j < 100000; j++) {
                        Observation observation = Observation.createNotStarted("test", observationRegistry);
                        observation.start();
                        observation.stop();
                    }
                });
            }
            for (int i = 0; i < 100; i++) {
                test[i].get();
            }
            log.info("cost {} ms", System.currentTimeMillis() - start);
        }
    }
}

在我的电脑上,这个测试最后输出显示大概需要 5300ms 左右。我们将全局的 ObservationHandler 改为什么都不做的,对比下:

package com.github.hashjang.wwsmbjysymrdo;

import io.micrometer.common.KeyValue;
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Tag;
import io.micrometer.core.instrument.Timer;
import io.micrometer.core.instrument.observation.MeterObservationHandler;
import io.micrometer.observation.Observation;
import io.micrometer.observation.ObservationRegistry;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;

@Log4j2
@SpringBootApplication
public class Main {
    public static void main(String[] args) {
        SpringApplication.run(Main.class);
    }

    @Bean
    MeterObservationHandler<Observation.Context> defaultMeterObservationHandler(MeterRegistry meterRegistry) {
        return new MeterObservationHandler<>() {
        };
    }

    @Component
    public static class Runner implements CommandLineRunner {
        @Autowired
        private ObservationRegistry observationRegistry;

        @Override
        public void run(String... args) throws Exception {
            ExecutorService executor = Executors.newFixedThreadPool(100);
            //预热
            for (int j = 0; j < 100000; j++) {
                executor.execute(() -> {
                    Observation observation = Observation.createNotStarted("test", observationRegistry);
                    observation.start();
                    observation.stop();
                });
            }
            //测试
            long start = System.currentTimeMillis();
            Future<?> test[] = new Future[100];
            for (int i = 0; i < 100; i++) {
                test[i] = executor.submit(() -> {
                    for (int j = 0; j < 100000; j++) {
                        Observation observation = Observation.createNotStarted("test", observationRegistry);
                        observation.start();
                        observation.stop();
                    }
                });
            }
            for (int i = 0; i < 100; i++) {
                test[i].get();
            }
            log.info("cost {} ms", System.currentTimeMillis() - start);
        }
    }
}

最后大概需要 1400 ms 左右。这个差距还是很明显的。

默认的 DefaultMeterObservationHandler

我们看一下 DefaultMeterObservationHandler 的源码:

hhttps://github.com/micrometer-metrics/micrometer/blob/main/micrometer-core/src/main/java/io/micrometer/core/instrument/observation/DefaultMeterObservationHandler.java

    @Override
    public void onStart(Observation.Context context) {
        LongTaskTimer.Sample longTaskSample = LongTaskTimer.builder(context.getName() + ".active")
                .tags(createTags(context)).register(meterRegistry).start();
        context.put(LongTaskTimer.Sample.class, longTaskSample);

        Timer.Sample sample = Timer.start(meterRegistry);
        context.put(Timer.Sample.class, sample);
    }

    @Override
    public void onStop(Observation.Context context) {
        Timer.Sample sample = context.getRequired(Timer.Sample.class);
        sample.stop(Timer.builder(context.getName()).tags(createErrorTags(context)).tags(createTags(context))
                .register(this.meterRegistry));

        LongTaskTimer.Sample longTaskSample = context.getRequired(LongTaskTimer.Sample.class);
        longTaskSample.stop();
    }

可以看出,默认情况下,DefaultMeterObservationHandler 会在 Observationstart 的时候,创建一个 LongTaskTimer.Sample 对象,然后将这个 LongTaskTimer.Sample 对象放入 LongTaskTimeractiveTasks 中,然后还有一个 Timer.Sample 对象,这个 Timer.Sample 对象是用于记录 Observation 的耗时的。

其中,LongTaskTimer.Sample 对象会引起如果 Observation 只 start 没有 stop 的话,会导致内存溢出,而 Timer.Sample 对象没有这个问题。并且,LongTaskTimer.Sample 对象的在 stop 的时候,因为多线程的原因可能 ConcurrentLinkedQueue 很大导致 CPU 飙高。

解决方案

我们可以替换掉 DefaultMeterObservationHandler,自己实现一个 MeterObservationHandler,在 start 的时候,不创建 LongTaskTimer.Sample 对象,只保留 Timer.Sample 对象,这样就不会出现内存溢出以及 CPU 飙高的问题了。

package com.github.hashjang.wwsmbjysymrdo;

import io.micrometer.common.KeyValue;
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Tag;
import io.micrometer.core.instrument.Timer;
import io.micrometer.core.instrument.observation.MeterObservationHandler;
import io.micrometer.observation.Observation;
import io.micrometer.observation.ObservationRegistry;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

@Log4j2
@SpringBootApplication
public class Main {
    public static void main(String[] args) {
        SpringApplication.run(Main.class);
    }

    @Bean
    MeterObservationHandler<Observation.Context> defaultMeterObservationHandler(MeterRegistry meterRegistry) {
        return new MeterObservationHandler<>() {
            @Override
            public void onStart(Observation.Context context) {
                Timer.Sample sample = Timer.start(meterRegistry);
                context.put(Timer.Sample.class, sample);
            }

            @Override
            public void onStop(Observation.Context context) {
                List<Tag> tags = createTags(context);
                tags.add(Tag.of("error", getErrorValue(context)));
                Timer.Sample sample = context.getRequired(Timer.Sample.class);
                sample.stop(Timer.builder(context.getName()).tags(tags).register(meterRegistry));
            }

            @Override
            public void onEvent(Observation.Event event, Observation.Context context) {
                Counter.builder(context.getName() + "." + event.getName())
                        .tags(createTags(context))
                        .register(meterRegistry)
                        .increment();
            }

            private String getErrorValue(Observation.Context context) {
                Throwable error = context.getError();
                return error != null ? error.getClass().getSimpleName() : "none";
            }

            private List<Tag> createTags(Observation.Context context) {
                List<Tag> tags = new ArrayList<>();
                for (KeyValue keyValue : context.getLowCardinalityKeyValues()) {
                    tags.add(Tag.of(keyValue.getKey(), keyValue.getValue()));
                }
                return tags;
            }
        };
    }

    @Component
    public static class Runner implements CommandLineRunner {
        @Autowired
        private ObservationRegistry observationRegistry;

        @Override
        public void run(String... args) throws Exception {
            ExecutorService executor = Executors.newFixedThreadPool(100);
            //预热
            for (int j = 0; j < 100000; j++) {
                executor.execute(() -> {
                    Observation observation = Observation.createNotStarted("test", observationRegistry);
                    observation.start();
                    observation.stop();
                });
            }
            //测试
            long start = System.currentTimeMillis();
            Future<?> test[] = new Future[100];
            for (int i = 0; i < 100; i++) {
                test[i] = executor.submit(() -> {
                    for (int j = 0; j < 100000; j++) {
                        Observation observation = Observation.createNotStarted("test", observationRegistry);
                        observation.start();
                        observation.stop();
                    }
                });
            }
            for (int i = 0; i < 100; i++) {
                test[i].get();
            }
            log.info("cost {} ms", System.currentTimeMillis() - start);
        }
    }
}

并且,针对这个问题,我们已经提交了 Issue,希望能够尽快采纳建议到 Micrometer 的主干分支中:Remove LongtaskTimer.Sample in DefaultMeterObservationHandler (for the purpose of prevent memory leak and lower CPU consuming)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1398768.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

传统语音识别系统流程

文章目录 概述语音识别原理公式语音识别术语&#xff1a;分帧提取声学特征声学模型 概述 语音识别传统方法主要分两个阶段&#xff1a;训练和识别&#xff0c;训练阶段主要是生成声学模型和语言模型给识别阶段用。传统方法主要有五大模块组成&#xff0c;分别是特征提取&#…

JVM 如何判断一个对象可以被回收

Hi&#xff0c; 我是 浮生。 今天分享一道一线互联网公司必问的面试题。 ”JVM 如何判断一个对象可以被回收“ 关于这个问题&#xff0c;来看看高手的回答。 一、问题解析 在 JVM 里面&#xff0c;要判断一个对象是否可以被回收&#xff0c;最重要的是判断这个对象是否还在被…

XHCMS靶场小记(熊海)

文件包含漏洞 template下的header.php中存在文件包含漏洞&#xff08;该文件被file文件夹下的多数文件进行包含&#xff09; f参数可以包含任意文件通过php格式解析&#xff08;这是文件包含点&#xff09; 代码分析 根目录下的index.php文件&#xff1b;r参数用于获取包含文…

怎样使用崭新的硬盘

新买的一块硬盘&#xff0c;接到电脑上&#xff0c;打开机器&#xff0c;却找不到新的硬盘&#xff0c;怎么回事&#xff1f;新的硬盘是坏的么&#xff1f;怎样才能把新硬盘用起来&#xff1f; 可能有几种原因导致您的电脑无法识别新的硬盘。以下是一些建议的解决方法&#xff…

SOCKET编程和TCP通信案例三次握手四次挥手

文章目录 一、SOCKET1、网络套接字SOCKET2、网络字节序2.1、小端法2.2、大端法2.3、字节序转换3、IP地址转换函数3.1、本地字节序转网络字节序3.1.1、函数原型&#xff1a;3.1.2、返回值3.2、网络字节序转本地字节序3.2.1、函数原型3.2.2、返回值4、sockaddr地址结构&#xff0…

Android Termux技能大揭秘:安装MySQL并实现公网远程连接

&#x1f308;个人主页&#xff1a;聆风吟 &#x1f525;系列专栏&#xff1a;网络奇遇记、Cpolar杂谈 &#x1f516;少年有梦不应止于心动&#xff0c;更要付诸行动。 文章目录 &#x1f4cb;前言一. 安装MariaDB二. 安装cpolar内网穿透工具三. 创建安全隧道映射mysql四. 公网…

Linux下安装docker

1、查看系统版本 Docker支持64位版本的CentOS 7和CentOS 8及更高版本&#xff0c;它要求Linux内核版本不低于3.10。查看Linux版本的命令这里推荐两种&#xff1a;lsb_release -a或cat /etc/redhat-release。 显然&#xff0c;当前Linux系统为CentOS7。再查一下内核版本是否不低…

MCM备赛笔记——蒙特卡罗方法

Key Concept 蒙特卡罗方法&#xff08;Monte Carlo Method&#xff09;&#xff0c;也称为统计模拟方法&#xff0c;是一种基于概率和统计的数值计算方法。该方法使用随机数&#xff08;或更常见的伪随机数&#xff09;来解决可能非常复杂的数学或物理问题。蒙特卡罗方法广泛应…

关于xftp突然无法连接服务器或虚拟机,可以ping通自己的虚拟机ip地址

关于xftp突然无法连接服务器或虚拟机,ping自己的虚拟机ip地址可以ping通 主机能ping通虚拟机&#xff08;ubuntu&#xff09; C:\Users\42216\Desktop>ping 192.168.61.128正在 Ping 192.168.61.128 具有 32 字节的数据: 来自 192.168.61.128 的回复: 字节32 时间<1ms …

链表的相交

链表的相交 力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台备战技术面试&#xff1f;力扣提供海量技术面试资源&#xff0c;帮助你高效提升编程技能&#xff0c;轻松拿下世界 IT 名企 Dream Offer。https://leetcode.cn/problems/intersection-of-tw…

selenium自动化测试框架简介

工欲善其事必先利其器&#xff0c;对于自动化测试也是同样的道理。进入自动化测试之前&#xff0c;怎么能不了解我们都有哪些框架、工具&#xff0c;又有哪些是主流的呢&#xff1f;1. 行业自动化测试框集 对于大多数同学来说&#xff0c;并不需要全面了解自动化测试工具都有哪…

大数据导论(3)---大数据技术

文章目录 1. 大数据技术概述2. 数据采集与预处理2.1 数据采集2.2 预处理 3. 数据存储和管理3.1 分布式基础架构Hadoop3.2 分布式文件系统HDFS3.3 分布式数据库HBase3.4 非关系型数据库NoSQL 4. 数据可视化与保护 1. 大数据技术概述 大数据技术主要包括数据采集与预处理、数据存…

Linux指令(四)

1.more指令 我们知道cat指令是用来读取文本文件的&#xff0c;但是如果是大文件&#xff0c;其实是不适合cat读取的&#xff0c;原因是&#xff1a;cat读取会直接到文本的结尾&#xff0c;所以我们引入&#xff1a;more指令 该指令不会将文件直接读到结尾&#xff0c;而是将最…

GZ036 区块链技术应用赛项赛题第3套

2023年全国职业院校技能大赛 高职组 “区块链技术应用” 赛项赛卷&#xff08;3卷&#xff09; 任 务 书 参赛队编号&#xff1a; 背景描述 新能源作为新兴领域&#xff0c;产业呈现碎片化与复杂化的特性&#xff0c;逐渐出现管理困难、供应链金融、可信监管与数…

在IDEA上运行成功,打包成jar包后,运行报错,程序自动退出

原因 java环境不正确&#xff0c;很有可能安装了多个环境&#xff0c;导致程序加载了错误程序。 解决办法 尝试修改环境变量&#xff0c;如果不行&#xff0c;建议删除掉多余的java环境。 注意&#xff1a;删除掉多余的Java环境需要用程序删除&#xff0c;直接删除文件&#xf…

史上最全软件测试面试题(含答案),进大厂涨薪必备

本试题分三部分&#xff0c;第一部分&#xff0c;基础面试题及答案&#xff0c;第二部分&#xff0c;高级进阶&#xff1b;第三部分&#xff0c;测试开发相关面试题&#xff0c;本篇为第一部分。 加粗样式建议&#xff0c;收藏后阅读&#xff0c;篇幅很长。 1、你的测试职业发…

用LED数码显示器循环显示数字0~9

#include<reg51.h> // 包含51单片机寄存器定义的头文件 /************************************************** 函数功能&#xff1a;延时函数&#xff0c;延时一段时间 ***************************************************/ void delay(void) { unsigned …

MCM备赛笔记——熵权法

Key Concept 熵权法是一种基于信息熵概念的权重确定方法&#xff0c;用于多指标决策分析中。信息熵是度量信息量的不确定性或混乱程度的指标&#xff0c;在熵权法中&#xff0c;它用来反映某个指标在评价过程中的分散程度&#xff0c;进而确定该指标的权重。指标的分散程度越高…

【SpringBoot】—— 如何创建SpringBoot工程

SpringBoot简化了Spring应用的初始搭建和开发过程。 工程创建 新建模块 出现java: 错误: 无效的源发行版&#xff1a;18这样的错误&#xff0c; 修改pom.xml文件 出现以下信息&#xff0c;即运行成功 修改默认端口 创建application.yml文件 内容&#xff1a; server:port:…

【Kafka】Linux本地和Docker安装Kafka

目录 Linux本地安装kafkajava环境配置Zookeeper的安装配置Kafka的安装与配置生产与消费 Docker安装kafkaZookeeper安装Kafka安装 Linux本地安装kafka java环境配置 1、上传jdk-8u261-linux-x64.rpm到服务器并安装&#xff1a; rpm -ivh jdk-8u261-linux-x64.rpm2、配置环境变…