StackWalker 遍历栈帧

StackWalker 遍历栈帧

  • 背景
  • StackWalker
  • StackFrame
  • Option
  • 方法
    • 创建 StackWalker
    • walk
      • 例:打印所有信息
      • 例:打印反射帧、隐藏帧
    • forEach
    • getCallerClass
      • 例:直接调用、反射调用
      • 例:栈底调用会抛异常
  • 参考

背景

在看 springboot 3.x 源码时发现 deduceMainApplicationClass 方法的实现发生了变化,用到了 StackWalker 来遍历栈帧,之前没用过。

image-20241203094528296

StackWalker

StackWalker 是线程安全的。多个线程可以共享单个 StackWalker 对象来遍历其自己的堆栈。

StackFrame 来代表栈帧,可以获取各种信息。

StackWalker. Option 来控制 StackFrame 可以获取哪些信息。

StackFrame

StackWalker 使用 StackFrame 类来表示栈帧的详细信息,但哪些方法可用受 java.lang.StackWalker.Option 的影响。

public interface StackFrame {/*** 返回声明 StackFrame 所代表方法的类全限定名。比如子类继承了父类但没重写父类的方法, 会返回父类的类名*/public String getClassName();/*** 返回 StackFrame 所代表方法的方法名** 若 Option 配置了 DROP_METHOD_INFO, 则抛异常 UnsupportedOperationException*/public String getMethodName();/*** 返回声明 StackFrame 所代表方法的 Class 对象。比如子类继承了父类但没重写父类的方法, 会返回父类的类对象** 若 Option 配置了 DROP_METHOD_INFO, 或没有配置 RETAIN_CLASS_REFERENCE, 则抛异常 UnsupportedOperationException*/public Class<?> getDeclaringClass();/*** 返回 StackFrame 所代表方法的 MethodType, 包含参数类型和返回值类型, 比如 String test(int a, String b, Object[] c) 的 MethodType 为 (int,String,Object[])String* 若 Option 配置了 DROP_METHOD_INFO, 或没有配置 RETAIN_CLASS_REFERENCE, 则抛异常 UnsupportedOperationException*/public default MethodType getMethodType() {throw new UnsupportedOperationException();}/*** 返回此 StackFrame 所表示的方法的描述符(如 Java 虚拟机规范所定义)。比如 String test(int a, String b, Object[] c) 的描述符为 (ILjava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;** 若 Option 配置了 DROP_METHOD_INFO, 或没有配置 RETAIN_CLASS_REFERENCE, 则抛异常 UnsupportedOperationException*/public default String getDescriptor() {throw new UnsupportedOperationException();}/*** 返回包含此栈帧表示的执行点的 code 属性的代码数组的索引。代码数组给出了实现该方法的 Java 虚拟机代码的实际字节。* * 若 Option 配置了 DROP_METHOD_INFO, 则抛异常 UnsupportedOperationException*/public int getByteCodeIndex();/*** 返回包含此栈帧表示的执行点的源文件的名称。通常,这对应于 Java 虚拟机规范定义的相关类文件的 SourceFile 属性。在某些系统中,该名称可能引用文件以外的某些源代码单元,例如源存储库中的条目。** 若 Option 配置了 DROP_METHOD_INFO, 则抛异常 UnsupportedOperationException*/public String getFileName();/*** 返回包含此栈帧表示的执行点的源代码的行号。通常,这是从 Java 虚拟机规范定义的相关类文件的 LineNumberTable 属性派生的。** 若 Option 配置了 DROP_METHOD_INFO, 则抛异常 UnsupportedOperationException*/public int getLineNumber();/*** 此栈帧表示的执行点的方法是否是 Native 方法* * 若 Option 配置了 DROP_METHOD_INFO, 则抛异常 UnsupportedOperationException*/public boolean isNativeMethod();/*** 为栈帧返回一个 StackTraceElement 对象** 若 Option 配置了 DROP_METHOD_INFO, 则抛异常 UnsupportedOperationException*/public StackTraceElement toStackTraceElement();
}

Option

public enum Option {/*** 在 StackFrames 中保留 Class 对象。* 使用此选项配置的 StackWalker 将支持 getCallerClass() 和 StackFrame.getDeclaringClass()。*/RETAIN_CLASS_REFERENCE,/*** 从 StackFrames 中删除方法信息。* 将无法调用 StackFrame 的 getMethodName()、getMethodType()、getLineNumber、getByteCodeIndex()、getFileName()、isNativeMethod() 方法** @since 22*/DROP_METHOD_INFO,/*** 显示所有反射帧。** 默认情况下,反射帧处于隐藏状态。配置了此 SHOW_REFLECT_FRAMES 选项的 StackWalker 将显示包含 java.lang.reflect.Method#invoke 和 java.lang.reflect.Constructor#newInstance(Object...) 及其反射实现类。SHOW_HIDDEN_FRAMES 选项也可用于显示所有反射帧,它还将显示特定于实现的其他隐藏帧。*/SHOW_REFLECT_FRAMES,/*** 显示所有的隐藏帧** 除了反射帧之外,Java 虚拟机实现还可以隐藏特定于实现的帧。具有此 SHOW_HIDDEN_FRAMES 选项的 StackWalker 将显示所有隐藏的帧(包括反射帧)。*/SHOW_HIDDEN_FRAMES;
}

方法

创建 StackWalker

StackWalker 可通过 4 个 getInstance 静态方法方法获取实例。estimateDepth 是要遍历的栈帧的估计数量,可以将其用作缓冲区大小的提示。Option 是什么介绍过的。

image-20241203100942997

StackWalker 有 3 个 public 方法:

walk

将给定的函数应用于当前线程的 StackFrames 流(串行流),从堆栈的栈顶开始遍历,栈顶即调用 walk 方法的方法(不是调用 StackWalker#getInstance 的方法)。

参数以一个 Function,其参数为 Stream<StackFrame>,且必须有返回值,可自由的遍历、操作 Stream

public <T> T walk(Function<? super Stream<StackFrame>, ? extends T> function)

例:打印所有信息

package com.example.jdk23;import java.util.EnumSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;public class Test {public static void main(String[] args) {m1();}public static void m1() {m2();}public static void m2() {m3();}public static void m3() {m4(StackWalker.getInstance(EnumSet.of(StackWalker.Option.RETAIN_CLASS_REFERENCE,StackWalker.Option.SHOW_REFLECT_FRAMES, StackWalker.Option.SHOW_HIDDEN_FRAMES)));}public static void m4(StackWalker stackWalker) {List<Map<String, Object>> stacks = stackWalker.walk(s -> s.map(Test::getStackInfo).toList());print(stacks);}public static Map<String, Object> getStackInfo(StackWalker.StackFrame frame) {LinkedHashMap<String, Object> info = new LinkedHashMap<>();info.put("className", frame.getClassName());info.put("methodName", frame.getMethodName());info.put("declaringClass", frame.getDeclaringClass());info.put("methodType", frame.getMethodType());info.put("descriptor", frame.getDescriptor());info.put("byteCodeIndex", frame.getByteCodeIndex());info.put("fileName", frame.getFileName());info.put("lineNumber", frame.getLineNumber());info.put("isNativeMethod", frame.isNativeMethod());info.put("stackTraceElement", frame.toStackTraceElement());return info;}private static void print(List<Map<String, Object>> stacks) {for (Map<String, Object> stack : stacks) {stack.forEach((k, v) -> System.out.println(k + ": " + v));System.out.println();}}
}

m3、m4 方法的 javap 结果

  public static void m3();descriptor: ()Vflags: (0x0009) ACC_PUBLIC, ACC_STATICCode:stack=3, locals=0, args_size=00: getstatic     #18                 // Field java/lang/StackWalker$Option.RETAIN_CLASS_REFERENCE:Ljava/lang/StackWalker$Option;3: getstatic     #24                 // Field java/lang/StackWalker$Option.SHOW_REFLECT_FRAMES:Ljava/lang/StackWalker$Option;6: getstatic     #27                 // Field java/lang/StackWalker$Option.SHOW_HIDDEN_FRAMES:Ljava/lang/StackWalker$Option;9: invokestatic  #30                 // Method java/util/EnumSet.of:(Ljava/lang/Enum;Ljava/lang/Enum;Ljava/lang/Enum;)Ljava/util/EnumSet;12: invokestatic  #36                 // Method java/lang/StackWalker.getInstance:(Ljava/util/Set;)Ljava/lang/StackWalker;15: invokestatic  #42                 // Method m4:(Ljava/lang/StackWalker;)V18: returnLineNumberTable:line 23: 0line 25: 18public static void m4(java.lang.StackWalker);descriptor: (Ljava/lang/StackWalker;)Vflags: (0x0009) ACC_PUBLIC, ACC_STATICCode:stack=2, locals=2, args_size=10: aload_01: invokedynamic #46,  0             // InvokeDynamic #0:apply:()Ljava/util/function/Function;6: invokevirtual #50                 // Method java/lang/StackWalker.walk:(Ljava/util/function/Function;)Ljava/lang/Object;9: checkcast     #54                 // class java/util/List12: astore_113: aload_114: invokestatic  #56                 // Method print:(Ljava/util/List;)V17: returnLineNumberTable:line 28: 0line 29: 6line 33: 13line 34: 17LocalVariableTable:Start  Length  Slot  Name   Signature0      18     0 stackWalker   Ljava/lang/StackWalker;13       5     1 stacks   Ljava/util/List;LocalVariableTypeTable:Start  Length  Slot  Name   Signature13       5     1 stacks   Ljava/util/List<Ljava/util/Map<Ljava/lang/String;Ljava/lang/Object;>;>;MethodParameters:Name                           FlagsstackWalker

输出:

第 5 行 descriptor 为 (Ljava/lang/StackWalker;)V,与 javap 结果的 18 行对应。

第 6 行 byteCodeIndex 为 6,从 javap 结果的 24 行可看出是调用 StackWalker.walk

第 8 行 lineNumber 为 29,从 javap 结果的 32 行可看出是与 code 6 对应,即调用 StackWalker.walk

className: com.example.jdk23.Test
methodName: m4
declaringClass: class com.example.jdk23.Test
methodType: (StackWalker)void
descriptor: (Ljava/lang/StackWalker;)V
byteCodeIndex: 6
fileName: Test.java
lineNumber: 29
isNativeMethod: false
stackTraceElement: com.example.jdk23.Test.m4(Test.java:29)className: com.example.jdk23.Test
methodName: m3
declaringClass: class com.example.jdk23.Test
methodType: ()void
descriptor: ()V
byteCodeIndex: 15
fileName: Test.java
lineNumber: 23
isNativeMethod: false
stackTraceElement: com.example.jdk23.Test.m3(Test.java:23)className: com.example.jdk23.Test
methodName: m2
declaringClass: class com.example.jdk23.Test
methodType: ()void
descriptor: ()V
byteCodeIndex: 0
fileName: Test.java
lineNumber: 19
isNativeMethod: false
stackTraceElement: com.example.jdk23.Test.m2(Test.java:19)className: com.example.jdk23.Test
methodName: m1
declaringClass: class com.example.jdk23.Test
methodType: ()void
descriptor: ()V
byteCodeIndex: 0
fileName: Test.java
lineNumber: 15
isNativeMethod: false
stackTraceElement: com.example.jdk23.Test.m1(Test.java:15)className: com.example.jdk23.Test
methodName: main
declaringClass: class com.example.jdk23.Test
methodType: (String[])void
descriptor: ([Ljava/lang/String;)V
byteCodeIndex: 0
fileName: Test.java
lineNumber: 11
isNativeMethod: false
stackTraceElement: com.example.jdk23.Test.main(Test.java:11)

从输出中可以看出栈顶是 m4,而非 m3

例:打印反射帧、隐藏帧

package com.example.jdk23;import java.util.EnumSet;
import java.util.List;
import java.util.stream.Stream;public class Test2 {public static void main(String[] args) {showReflectFrames();System.out.println();showHiddenFrames();}static void showReflectFrames() {try {Test2.class.getDeclaredMethod("showReflectFrames0").invoke(null);} catch (Exception e) {throw new RuntimeException(e);}}static void showReflectFrames0() {List<StackWalker.StackFrame> frames = StackWalker.getInstance(EnumSet.of(StackWalker.Option.SHOW_REFLECT_FRAMES)).walk(Stream::toList);for (StackWalker.StackFrame frame : frames) {System.out.println(frame);}}static void showHiddenFrames() {try {Test2.class.getDeclaredMethod("showHiddenFrames0").invoke(null);} catch (Exception e) {throw new RuntimeException(e);}}static void showHiddenFrames0() {List<StackWalker.StackFrame> frames = StackWalker.getInstance(EnumSet.of(StackWalker.Option.SHOW_HIDDEN_FRAMES)).walk(Stream::toList);for (StackWalker.StackFrame frame : frames) {System.out.println(frame);}}
}

showReflectFrames0 的输出包含 java.lang.invokejdk.internal.reflect 帧。

showHiddenFrames0 的输出比 showReflectFrames0 多了一些帧,这些是隐藏帧。

com.example.jdk23.Test2.showReflectFrames0(Test2.java:26)
java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
java.base/java.lang.reflect.Method.invoke(Method.java:580)
com.example.jdk23.Test2.showReflectFrames(Test2.java:18)
com.example.jdk23.Test2.main(Test2.java:10)com.example.jdk23.Test2.showHiddenFrames0(Test2.java:42)
java.base/java.lang.invoke.DirectMethodHandle$Holder.invokeStatic(DirectMethodHandle$Holder)
java.base/java.lang.invoke.LambdaForm$MH/0x00000296bd003400.invoke(LambdaForm$MH)
java.base/java.lang.invoke.Invokers$Holder.invokeExact_MT(Invokers$Holder)
java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invokeImpl(DirectMethodHandleAccessor.java:153)
java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
java.base/java.lang.reflect.Method.invoke(Method.java:580)
com.example.jdk23.Test2.showHiddenFrames(Test2.java:34)
com.example.jdk23.Test2.main(Test2.java:12)

forEach

对当前线程的 StackFrame 流的每个元素执行给定的操作,从栈顶开始遍历,栈顶即调用 forEach 方法的方法(不是调用 StackWalker#getInstance 的方法)。该方法等效于调用 walk(s -> { s. forEach(action); return null; });

参数是一个 Consumer,其参数是 StackFrame,只能顺序遍历栈帧,不像 walk 方法可以自由的操作 Stream<StackFrame>

public void forEach(Consumer<? super StackFrame> action)

例子

public class Test2 {public static void main(String[] args) {m1();}public static void m1() {m2();}public static void m2() {m3();}public static void m3() {m4(StackWalker.getInstance(EnumSet.of(StackWalker.Option.RETAIN_CLASS_REFERENCE,StackWalker.Option.SHOW_REFLECT_FRAMES, StackWalker.Option.SHOW_HIDDEN_FRAMES)));}public static void m4(StackWalker stackWalker) {stackWalker.forEach(System.out::println);}
}

输出

com.example.jdk23.Test2.m4(Test2.java:34)
com.example.jdk23.Test2.m3(Test2.java:29)
com.example.jdk23.Test2.m2(Test2.java:25)
com.example.jdk23.Test2.m1(Test2.java:21)
com.example.jdk23.Test2.main(Test2.java:17)

getCallerClass

获取调用者的 Class 对象,假设调用 getCallerClass 方法的方法为 a ,调用方法 a 的方法为 b,那方法 b 就是调用者。

此方法会过滤反射帧、java. lang. invoke. MethodHandle 和隐藏帧,而不管 StackWalker 是否配置了 SHOW_REFLECT_FRAMESSHOW_HIDDEN_FRAMES 选项。

只有在调用者帧存在时才能调用此方法。如果从栈上最底部的帧调用此方法,则会引发 IllegalCallerException,比如在 main 方法中调用。

若 Option 配置了 DROP_METHOD_INFO, 或没有配置 RETAIN_CLASS_REFERENCE, 则抛异常 UnsupportedOperationException。

public Class<?> getCallerClass()

例:直接调用、反射调用

package com.example.jdk23;import java.util.EnumSet;public class Test4 {static class C1 {static void m1() {C2.m2();}}static class C2 {static void m2() {C.m();try {C.class.getDeclaredMethod("m").invoke(null);} catch (Exception e) {throw new RuntimeException(e);}}}static class C {static void m() {System.out.println(StackWalker.getInstance(EnumSet.of(StackWalker.Option.RETAIN_CLASS_REFERENCE,StackWalker.Option.SHOW_REFLECT_FRAMES, StackWalker.Option.SHOW_HIDDEN_FRAMES)).getCallerClass());}}public static void main(String[] args) {C1.m1();C2.m2();C.m();}
}

直接调用、反射调用的结果是相同的,确实隐藏了反射帧、隐藏帧。

class com.example.jdk23.Test4$C2
class com.example.jdk23.Test4$C2
class com.example.jdk23.Test4$C2
class com.example.jdk23.Test4$C2
class com.example.jdk23.Test4

例:栈底调用会抛异常

package com.example.jdk23;public class Test3 {public static void main(String[] args) {StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE).getCallerClass();}
}
Exception in thread "main" java.lang.IllegalCallerException: no caller frame: [com.example.jdk23.Test3 , null]at java.base/java.lang.StackStreamFactory$CallerClassFinder.consumeFrames(StackStreamFactory.java:752)at java.base/java.lang.StackStreamFactory$CallerClassFinder.consumeFrames(StackStreamFactory.java:713)at java.base/java.lang.StackStreamFactory$AbstractStackWalker.doStackWalk(StackStreamFactory.java:330)at java.base/java.lang.StackStreamFactory$AbstractStackWalker.callStackWalk(Native Method)at java.base/java.lang.StackStreamFactory$AbstractStackWalker.beginStackWalk(StackStreamFactory.java:424)at java.base/java.lang.StackStreamFactory$AbstractStackWalker.walkHelper(StackStreamFactory.java:267)at java.base/java.lang.StackStreamFactory$AbstractStackWalker.walk(StackStreamFactory.java:259)at java.base/java.lang.StackStreamFactory$CallerClassFinder.findCaller(StackStreamFactory.java:725)at java.base/java.lang.StackWalker.getCallerClass(StackWalker.java:649)at com.example.jdk23.Test3.main(Test3.java:5)

参考

Java 9 揭秘(16. 虚拟机栈遍历) - 林本托 - 博客园

Java 9 StackWalking API入门介绍 | Baeldung中文网

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

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

相关文章

捷联惯导原理和算法预备知识

原理和算法预备知识 牛顿第一运动定律-惯性定律&#xff1a;如一物体不受外力作用&#xff0c;它将保持静止状态或匀速直线运动状态不变。 牛顿第二运动定律&#xff1a;表达式为Fma,。其中F为物体所受的合力&#xff0c;m为物体的质量&#xff0c;a为物体的加速度。这个公式…

便捷工具--ssh登录ubuntu

一、概述 由于ubuntu终端的使用会有诸多不便捷的地方&#xff0c;建议使用mobaterm、xshell、SecureCRT等软件&#xff0c;通过ssh方式&#xff0c;操作虚拟机的ubuntu系统。 1、ssh的安装 sudo apt install openssh-server2、查看ubuntu的ip 3、ssh端登录 ssh链接linux端的…

【白盒测试】单元测试的理论基础及用例设计技术(6种)详解

目录 &#x1f31e;前言 &#x1f3de;️1. 单元测试的理论基础 &#x1f30a;1.1 单元测试是什么 &#x1f30a;1.2 单元测试的好处 &#x1f30a;1.3 单元测试的要求 &#x1f30a;1.4 测试框架-Junit4的介绍 &#x1f30a;1.5 单元测试为什么要mock &#x1f3de;️…

【案例分享】高性能AI边缘计算赋能车端真值系统​

近年来&#xff0c;智能驾驶行业正在蓬勃发展&#xff0c;对于研发完成的智能驾驶车辆&#xff0c;需要对其进行全方面的测试才能商用量产&#xff0c;以确保用户的人身财产安全。将测试车辆直接进行实际道路测试将面临安全性&#xff0c;经济性&#xff0c;场地可靠性&#xf…

【docker】11. 容器实战案例

综合实战一&#xff1a;Mysql 容器化安装 进入 mysql 的镜像网站&#xff0c;查找 mysql 的镜像 mysql docker hub 官网 可以看到有这么多的 tag 我们选择使用最多的 5.7 版本&#xff0c;拉取镜像 root139-159-150-152:/data/myworkdir/container# docker pull mysql:5.7 5.…

全新图文对、视频文本对数据集,高效赋能多模态大模型训练任务

海天瑞声11月数据集上新&#xff01;这次推出的数据集包括语音识别、语音合成、多模态等领域&#xff0c;可用于多模态大模型训练任务&#xff0c;开发者可轻松应对数据瓶颈&#xff0c;高效提升模型性能。 印度尼西亚语语音识别数据集 泰语语音识别数据集 温柔贴心中文女声语…

ES集群规模与角色规划

业务场景需求 业务特征 目前日志统计分析集群具有以下关键特征&#xff1a; 延迟要求&#xff1a;30秒以内并发性能&#xff1a;高并发读写数据容错&#xff1a;可容忍少量数据丢失 数据规模 每日原始日志采集量&#xff1a;约150GB数据查询范围&#xff1a; 近期数据&…

[Redis#14] 持久化 | RDB | bgsave | check-rdb | 灾备

目录 0.概述 持久化的策略 1 RDB 1.1 触发机制 1.2 流程说明 1.3 RDB 的优缺点 0.概述 在学习 MySQL 数据库时&#xff0c;我们了解到事务的四个核心特性&#xff1a;原子性、一致性、持久性和隔离性。这些特性确保了数据库操作的安全性和可靠性。当我们转向 Redis 时&a…

Modern Effective C++ 条款二十九三十:移动语义和完美转发失败的情况

条款二十九&#xff1a;假定移动操作不存在&#xff0c;成本高&#xff0c;未被使用 移动语义可以说是C11最主要的特性。"移动容器和拷贝指针一样开销小"&#xff0c;"拷贝临时对象现在如此高效&#xff0c;“写代码避免这种情况简直就是过早优化"。很多开…

C++【模板】plus

目录 一、非类型模板参数 1.引入 2.使用 二、模板特化 1.函数模板特化 2.特化失效 3.类模板特化 应用 三、*带模板的分离编译 一、非类型模板参数 1.引入 我们使用宏对某个变量进行定值&#xff0c;如 #define N10 --->那么N在下面使用时始终为10&#xff0c;如果…

Leetcode 每日一题 290.单词规律

目录 一、问题分析 二、解题思路 三、代码实现 四、复杂度分析 五、总结 在编程的世界里&#xff0c;我们常常会遇到各种有趣的字符串匹配问题。今天要探讨的就是这样一个问题&#xff1a;给定一种规律 pattern 和一个字符串 s&#xff0c;判断 s 是否遵循与 pattern 相同…

浅谈FRTC8563M实时时钟芯片

FRTC8563M是NYFEA徕飞公司推出的一款实时时钟芯片和日历芯片&#xff0c;采用MSOP-8封装形式。它具有低功耗特性&#xff0c;适用于电池供电的便携式设备。该芯片提供年、月、日、星期、小时、分钟和秒的计时功能&#xff0c;并且具有闹钟功能。FRTC8563M通过I2C总线与微控制器…

HOC vs Render Props vs Hooks

相关问题 什么是 HOC / Render Props / Hooks为什么需要 HOC / Render Props / Hooks如何提高代码复用性Hooks 的实现原理Hooks 相比其他方案有什么优势 关键点 复用性HOC / Render Props / Hooks 三种写法都可以提高代码的复用性&#xff0c;但实现方法不同&#xff1a; H…

【每天一篇深度学习论文】2024多级卷积模块MCM

目录 论文介绍题目&#xff1a;论文地址&#xff1a; 创新点方法模型总体架构双流编码器特征融合模块解码器 核心模块描述多尺度感知融合模块&#xff08;MAFM&#xff09;全局融合模块&#xff08;GFM&#xff09;多级卷积模块&#xff08;MCM&#xff09; 即插即用模块作用特…

Play with docker 使用ssh命令远程登录时Permission denied (publickey)

可以看到这里使用的是 ssh-ed25519 在本机生成对应密钥: ssh-keygen -t ed25519 -P "" -f ~/.ssh/id_ed25519 然后再尝试远程连接就好了。 参考:无法通过SSH连接到码头游乐场中的实例-腾讯云开发者社区-腾讯云

我眼中的“懂重构”(一)

初识重构 2017年的时候&#xff0c;领导让我看公司的一本书《重构——改善代码的既有设计》&#xff0c;这是一本JAVA版本的&#xff0c;前后看了2遍。那时候看书因为不懂看的格外仔细。我只是那时候不懂&#xff0c;然而多年后的今天我仍然发现很多人对重构充满误解。在刚进入…

数字图像处理(15):图像灰度反转和彩色反转

&#xff08;1&#xff09;图像反转&#xff1a;是指对图像的颜色信息进行相反的处理&#xff0c;从而得到一个新的图像。在计算机视觉和图像处理领域&#xff0c;图像反转是一种常见的操作&#xff0c;它可以帮助我们实现不同的图像特效和视觉效果。 &#xff08;2&#xff09…

Ubuntu系统上mysql服务部署

前段时间搞了一个mysql服务端的部署&#xff0c;在Ubuntu系统上&#xff0c;中间也踩了许多坑&#xff0c;特此记录下。 下载 官网&#xff1a;MySQL :: MySQL Community Downloads 这个里面有不同系统的安装包&#xff0c;根据自己的系统选择&#xff0c;我选了 MySQL Com…

linux 服务器 一次性查看 CPU、内存和磁盘使用情况

创建 vi check_usage.sh #!/bin/bashecho " CPU 使用率 " mpstat -P ALL 1 1echo -e "\n 内存使用情况 " free -hecho -e "\n 磁盘使用率 " df -h执行授权 chmod x check_usage.sh执行查看 ./check_usage.sh这样可以快速获取系统资源的概览。…

Unity HDRP Water Surface 水系统 基础教程

Unity HDRP Water Surface 水系统 基础教程 Unity Water SurfaceUnity 项目创建Unity Water Surface&#xff1a;Ocean&#xff08;海洋&#xff09;简介Ocean&#xff1a;Transform、GeneralOcean&#xff1a;Simulation&#xff08;仿真模拟&#xff09;Ocean&#xff1a;Sim…