Java线上监控诊断产品Arthas

news2024/11/25 16:18:16

最近一直在研究Java的动态追踪技术,碰到了Arthas,正好以前也想学,趁此机会就了解了一下。
什么是Arthas?首先我们看看Arthas官方文档是怎么描述的:

什么是Arthas

Arthas 是一款线上监控诊断产品,通过全局视角实时查看应用 load、内存、gc、线程的状态信息,并能在不修改应用代码的情况下,对业务问题进行诊断,包括查看方法调用的出入参、异常,监测方法执行耗时,类加载信息等,大大提升线上问题排查效率

Arthas能为你做什么

Arthas 是 Alibaba 开源的 Java 诊断工具,深受开发者喜爱。
当你遇到以下类似问题而束手无策时,Arthas可以帮助你解决:

  1. 这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception?
  2. 我改的代码为什么没有执行到?难道是我没 commit?分支搞错了?
  3. 遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗?
  4. 线上遇到某个用户的数据处理有问题,但线上同样无法 debug,线下无法重现!
  5. 是否有一个全局视角来查看系统的运行状况?
  6. 有什么办法可以监控到 JVM 的实时运行状态?
  7. 怎么快速定位应用的热点,生成火焰图?
  8. 怎样直接从 JVM 内查找某个类的实例?

下面我们直接进入正题。

前期准备工作

我们首先准备一个简单的http访问链接。

package wq.demo.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/test")
public class DockerController {

    @RequestMapping("/one")
    public String testOne(String name) {
        return name;
    }
}

然后maven打包放到我们的linux服务器上面

[root@VM-12-12-centos ~]# cd /data/arthas-test/

我们的jar包叫demo-0.0.1-SNAPSHOT.jar

[root@VM-12-12-centos arthas-test]# ls
demo-0.0.1-SNAPSHOT.jar

后台启动我们的项目

[root@VM-12-12-centos arthas-test]# nohup java -jar demo-0.0.1-SNAPSHOT.jar &

下载安装Arthas

Arthas提供了两种下载方式,第一种是下载jar包,第二种是下载全量包,本文用的是第一种。我们找一个常用的路径,用curl下载

[root@VM-12-12-centos ~]# curl -O https://arthas.aliyun.com/arthas-boot.jar
[root@VM-12-12-centos ~]# ls
arthas-boot.jar

可以看到我们的已经下载好了,名字叫arthas-boot.jar,接下来用java -jar启动

[root@VM-12-12-centos ~]# java -jar arthas-boot.jar 
[INFO] JAVA_HOME: /data/java/jdk1.8.0_181/jre
[INFO] arthas-boot version: 3.6.7
[INFO] Found existing java process, please choose one and input the serial number of the process, eg : 1. Then hit ENTER.
* [1]: 19401 demo-0.0.1-SNAPSHOT.jar

我们可以看到,在输出的最下面一行,有个[1]号进程,这就是我们刚刚启动的demo,Arthas启动的时候会检测所有的java进程,并且用数字的形式排列出来,我么需要监听什么,就输入数字就行了,一次只能监听一个进程。

[root@VM-12-12-centos ~]# java -jar arthas-boot.jar 
[INFO] JAVA_HOME: /data/java/jdk1.8.0_181/jre
[INFO] arthas-boot version: 3.6.7
[INFO] Found existing java process, please choose one and input the serial number of the process, eg : 1. Then hit ENTER.
* [1]: 19401 demo-0.0.1-SNAPSHOT.jar
1
[INFO] arthas home: /root/.arthas/lib/3.6.7/arthas
[INFO] Try to attach process 19401
Picked up JAVA_TOOL_OPTIONS: 
[INFO] Attach process 19401 success.
[INFO] arthas-client connect 127.0.0.1 3658
  ,---.  ,------. ,--------.,--.  ,--.  ,---.   ,---.                           
 /  O  \ |  .--. ''--.  .--'|  '--'  | /  O  \ '   .-'                          
|  .-.  ||  '--'.'   |  |   |  .--.  ||  .-.  |`.  `-.                          
|  | |  ||  |\  \    |  |   |  |  |  ||  | |  |.-'    |                         
`--' `--'`--' '--'   `--'   `--'  `--'`--' `--'`-----'                          

wiki       https://arthas.aliyun.com/doc                                        
tutorials  https://arthas.aliyun.com/doc/arthas-tutorials.html                  
version    3.6.7                                                                
main_class                                                                      
pid        19401                                                                
time       2023-04-14 14:38:23                                                  

[arthas@19401]$ 

当出现Arthas彩色字体的时候,说明监听成功,此时我们的用户切换成了arthas。在这个界面我们能做很多操作,具体的可以看上面的官方文档,这里我就列举一下常用的指令。

1.dashboard:当前系统的实时数据面板,按 ctrl+c 退出。

参数-i:刷新时间间隔,-n:刷新次数
在这里插入图片描述
数字字段说明:
ID: Java 级别的线程 ID,注意这个 ID 不能跟 jstack 中的 nativeID 一一对应。
NAME: 线程名
GROUP: 线程组名
PRIORITY: 线程优先级, 1~10 之间的数字,越大表示优先级越高
STATE: 线程的状态
CPU%: 线程的 cpu 使用率。比如采样间隔 1000ms,某个线程的增量 cpu 时间为 100ms,则 cpu 使用率=100/1000=10%
DELTA_TIME: 上次采样之后线程运行增量 CPU 时间,数据格式为秒
TIME: 线程运行总 CPU 时间,数据格式为分:秒
INTERRUPTED: 线程当前的中断位状态
DAEMON: 是否是 daemon 线程

2.jvm:查看当前 JVM 信息

在这里插入图片描述

Thread相关参数:
COUNT: JVM 当前活跃的线程数
DAEMON-COUNT: JVM 当前活跃的守护线程数
PEAK-COUNT: 从 JVM 启动开始曾经活着的最大线程数
STARTED-COUNT: 从 JVM 启动开始总共启动过的线程次数
DEADLOCK-COUNT: JVM 当前死锁的线程数

3.mc :编译.java文件为.class文件

mc 命令有可能失败。如果编译失败可以在本地编译好.class文件,再上传到服务器。失败的原因,其中之一是本地的jdk版本和linux服务器的jdk版本不一致。

4.redefine和retransform

这两个指令是比较常用的,都是加在外部的.class文件,去重载已经jvm已经加在过的类,通常用于不重启项目的情况下热更文件。因为我jdk版本的原因,所以我直接修改java文件,然后编译成class文件。
修改我们的测试controller

@RestController
@RequestMapping("/test")
public class DockerController {

    @RequestMapping("/one")
    public String testOne(String name) {
        return "hello word" + name;
    }
}

上传class文件:DockerController.class

[root@VM-12-12-centos arthas-test]# ls
arthas-output  demo-0.0.1-SNAPSHOT.jar  DockerController.class  nohup.out

然后重载

[arthas@26621]$ retransform /data/arthas-test/DockerController.class 
retransform success, size: 1, classes:
wq.demo.controller.DockerController
[arthas@26621]$ 

当出现retransform success, size: 1,说明热更成功,我们浏览器请求一下
在这里插入图片描述
可以看到代码已经热更成功,注意,这里的热更类似于idea右键的Compile And Reload File,不能修改方法体,常量等。

5.sm:查看已加载类的方法信息

[arthas@26621]$ sm -d wq.demo.controller.DockerController 
 declaring-class   wq.demo.controller.DockerController                                                                                                                                                                                           
 constructor-name  <init>                                                                                                                                                                                                                        
 modifier          public                                                                                                                                                                                                                        
 annotation                                                                                                                                                                                                                                      
 parameters                                                                                                                                                                                                                                      
 exceptions                                                                                                                                                                                                                                      
 classLoaderHash   31221be2                                                                                                                                                                                                                      

 declaring-class  wq.demo.controller.DockerController                                                                                                                                                                                            
 method-name      testOne                                                                                                                                                                                                                        
 modifier         public                                                                                                                                                                                                                         
 annotation       org.springframework.web.bind.annotation.RequestMapping                                                                                                                                                                         
 parameters       java.lang.String                                                                                                                                                                                                               
 return           java.lang.String                                                                                                                                                                                                               
 exceptions                                                                                                                                                                                                                                      
 classLoaderHash  31221be2                                                                                                                                                                                                                       

Affect(row-cnt:2) cost in 12 ms.

也可以用通配符,sm -d *Controller,查看所有Controller结尾的类,-d:展示每个方法的详细信息

6.watch:很常用的一个指令,监听请求的参数和返回

[arthas@26621]$ watch wq.demo.controller.DockerController testOne params -x 3

主要分三个结构,watch 类 方法名 监听范围 ,-x监听参数深度,比如我这个就是监听wq.demo.controller.DockerController类的testOne 方法的params(参数值),我在浏览器请求:
http://101.34.174.*:8080/test/one?name=watch,传参watch,可以看到,已经有了打印

method=wq.demo.controller.DockerController.testOne location=AtExit
ts=2023-04-14 15:10:44; [cost=0.036007ms] result=@Object[][
    @String[watch],
]

当然监听类容可以用ognl表达式:“{params,returnObj}”,就表示监听参数和返回值。

7.stack:输出方法的调用过程

我们还是以testOne方法为例

[arthas@26621]$ stack wq.demo.controller.DockerController testOne
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 21 ms, listenerId: 4
ts=2023-04-14 15:15:04;thread_name=http-nio-8080-exec-9;id=18;is_daemon=true;priority=5;TCCL=org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader@7c417213
    @wq.demo.controller.DockerController.testOne()
        at sun.reflect.NativeMethodAccessorImpl.invoke0(NativeMethodAccessorImpl.java:-2)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190)
        at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138)
        at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:105)
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:878)
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:792)
        at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
        at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040)
        at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943)
        at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
        at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:626)
        at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:733)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
        at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
        at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
        at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
        at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:143)
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:357)
        at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:374)
        at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
        at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:893)
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1707)
        at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.lang.Thread.run(Thread.java:748)

因为我们这里方法比较简单,所以看不出什么来,实际环境就正常了。

8.trace:方法内部调用路径,并输出方法路径上的每个节点上耗时

[arthas@26621]$ trace wq.demo.controller.DockerController testOne
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 40 ms, listenerId: 5
`---ts=2023-04-14 15:17:12;thread_name=http-nio-8080-exec-3;id=12;is_daemon=true;priority=5;TCCL=org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader@7c417213
    `---[0.054142ms] wq.demo.controller.DockerController:testOne()

可以看到我们请求只用了0.054142ms。

9.jad:反编译指定已加载类的源码

$ jad java.lang.String

ClassLoader:

Location:


        /*
         * Decompiled with CFR.
         */
        package java.lang;

        import java.io.ObjectStreamField;
        import java.io.Serializable;
...
        public final class String
        implements Serializable,
        Comparable<String>,
        CharSequence {
            private final char[] value;
            private int hash;
            private static final long serialVersionUID = -6849794470754667710L;
            private static final ObjectStreamField[] serialPersistentFields = new ObjectStreamField[0];
            public static final Comparator<String> CASE_INSENSITIVE_ORDER = new CaseInsensitiveComparator();
...
            public String(byte[] byArray, int n, int n2, Charset charset) {
/*460*/         if (charset == null) {
                    throw new NullPointerException("charset");
                }
/*462*/         String.checkBounds(byArray, n, n2);
/*463*/         this.value = StringCoding.decode(charset, byArray, n, n2);
            }
...

暂时先写这么多了,更多请参考Arthas官方文档

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

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

相关文章

欧拉函数及其线性筛

一&#xff0c;定义 欧拉函数是对于n小于或者等于他的数中与n互质的数的个数。一般用φ(x)表示。 二&#xff0c;欧拉函数公式 其中pi为n的所有质因数。 公式的理解方法可以是pi是与n互质的数&#xff0c;那么它&#xff08;包括它的倍数&#xff09;在1~n里面是均匀出现的&…

分布式场景下,Apache YARN、Google Kubernetes 如何解决资源管理问题?

所有的资源管理系统都需要解决资源的有效利用、任务的有效响应、调度策略的灵活配置这三个最基本问题。那么在分布式的场景下&#xff0c;YARN和Kubernetes是怎么解决的呢&#xff1f;本篇进行介绍。 — Apache YARN — YARN全称为&#xff08;Yet Another Resource Negotiato…

OSPF开放式最短路径优先协议

目录标题OSPF协议OSPF的数据包---5种OSPF的状态机OSPF的工作过程OSPF的基本配置关于ospf协议从邻居建立成为邻接的条件ospf的接口网络类型OSPF协议 是是无类别链路状态型IGP协议&#xff1b;由于其基于拓扑进行更新收敛&#xff0c;故更新量会随着拓扑的变大而呈指数上升&…

处理CSV(python)

处理CSV&#xff08;python&#xff09;简介1. CSV和Python简介2. 文章内容简介一、用csv模块读取和写入CSV文件1. CSV模块2. 示例二、用pandas库读取和写入CSV文件1. pandas2. 示例三、处理CSV文件中的特殊情况1. 特殊情况及处理方法2. 示例简介 1. CSV和Python简介 CSV是一…

动态内存管理--从动态内存分配函数开始和你一起了解

目录前言1.为什么存在动态内存分配2.动态内存函数的介绍2.1malloc函数和free函数2.2calloc函数2.3realloc函数3.常见的动态内存错误3.1对NULL指针的解引用操作3.2对动态开辟空间的越界访问3.3对非动态内存开辟的内存使用free释放3.4使用free释放一块动态内存的一部分3.5对同一块…

【致敬未来的攻城狮计划】— 连续打卡第三天:欲速则不达,今天是对RA2E1 基础知识的补充学习。

系列文章目录 1.连续打卡第一天&#xff1a;提前对CPK_RA2E1是瑞萨RA系列开发板的初体验&#xff0c;了解一下 2.开发环境的选择和调试&#xff08;从零开始&#xff0c;加油&#xff09; 文章目录 目录 系列文章目录 文章目录 前言 一、RA是什么&#xff1f; 二、RA特点…

RHCE——shell脚本练习

一.实验要求 1、判断web服务是否运行&#xff08;1、查看进程的方式判断该程序是否运行&#xff0c;2、通过查看端口的方式判断该程序是否运行&#xff09;&#xff0c;如果没有运行&#xff0c;则启动该服务并配置防火墙规则。 ​2、使用curl命令访问第二题的web服务&#xff…

Kafka的历史版本对应SpringBoot版本

截至目前&#xff08;2023年&#xff09;&#xff0c;Kafka的最新版本是2.9.0&#xff0c;发布于2022年11月30日。Kafka的历史版本可以在Kafka官方网站的下载页面中找到。Kafka从0.8版本开始发布&#xff0c;经历了多个版本的迭代和升级。以下是一些比较重要的Kafka版本及其发布…

Python实现哈里斯鹰优化算法(HHO)优化Catboost回归模型(CatBoostRegressor算法)项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档视频讲解&#xff09;&#xff0c;如需数据代码文档视频讲解可以直接到文章最后获取。 1.项目背景 2019年Heidari等人提出哈里斯鹰优化算法(Harris Hawk Optimization, HHO)&#xff0c;该算法有较强的全…

【LeetCode】剑指 Offer 52. 两个链表的第一个公共节点 p253 -- Java Version

题目链接&#xff1a;https://leetcode.cn/problems/liang-ge-lian-biao-de-di-yi-ge-gong-gong-jie-dian-lcof/ 1. 题目介绍&#xff08;52. 两个链表的第一个公共节点&#xff09; 输入两个链表&#xff0c;找出它们的第一个公共节点。 如下面的两个链表&#xff1a; 在节点…

基于微信小程序开发的“校园帮”系统

基于微信小程序开发的“校园帮”系统【毕业论文&#xff0c;源码】 本系统使用了java和mysql结合的结构开发了微信小程序应用&#xff0c;系统中所有和数据库有关系的操作都通过一个通用类来实现&#xff0c;大大提高了代码的耦合性&#xff0c;当数据库类型等信息变化后直接修…

【剑指offer|6.寻找峰值】

0.寻找峰值 关键点: 返回任意一个峰值的下标即可nums[-1]nums[n]负无穷 输入&#xff1a;nums [1,2,3,1] 输出&#xff1a;2 解释&#xff1a;3 是峰值元素&#xff0c;你的函数应该返回其索引 2 1.傻瓜编程(纯属玩乐) class Solution { public:int findPeakElement(vector&l…

普通人在家就能用ChatGPT轻松月赚$5000美金的方法

太震撼了&#xff0c;这简直就是下个世纪才应该出现的产品&#xff0c;突然之间我感觉就像人类&#xff0c;刚刚发明了电灯一样&#xff0c;一切都要变了&#xff0c;而且变的速度太快&#xff0c;让我都觉得有点茫然了&#xff0c;绝对就是技术大爆炸。今天这篇文章我想通过ch…

React 的源码与原理解读(六):reconcileChildren 与 DIFF 算法

写在专栏开头&#xff08;叠甲&#xff09; 作者并不是前端技术专家&#xff0c;也只是一名喜欢学习新东西的前端技术小白&#xff0c;想要学习源码只是为了应付急转直下的前端行情和找工作的需要&#xff0c;这篇专栏是作者学习的过程中自己的思考和体会&#xff0c;也有很多参…

常年不卷,按时下班,工作能力强,同事求助知无不言,不扯皮,不拉帮结派,这样的职场清流竟然被裁掉了!...

在职场上&#xff0c;你永远想不到什么样的员工会被优化&#xff0c;比如下面这位&#xff1a;常年不卷&#xff0c;按时下班&#xff0c;工作很专业&#xff0c;同事问什么都回答&#xff0c;不扯皮&#xff0c;不拉帮结派&#xff0c;简直是职场清流。在上个月竟然被优化了&a…

一分钟腾讯云轻量应用服务器性能评测(慎入坑)

腾讯云轻量应用服务器性能评测&#xff0c;轻量服务器CPU主频、处理器型号、公网带宽、月流量、Ping值测速、磁盘IO读写及使用限制&#xff0c;轻量应用服务器CPU内存性能和标准型云服务器CVM处于同一水准&#xff0c;所以大家不要担心轻量应用服务器的性能&#xff0c;腾讯云百…

JavaEE企业级应用开发教程——第十章 初识Spring MVC框架(黑马程序员第二版)(SSM)

第十章 初识Spring MVC框架 JSP Model2架构模型是一种将页面显示、流程控制和业务逻辑分离的Web应用程序架构模型&#xff0c;采用JSP、Servlet和JavaBean技术实现。但是&#xff0c;它将通用逻辑以硬编码的方式实现&#xff0c;每次开发新的Web应用程序时都需要重新编写Servl…

MyBatis注解开发---实现增删查改和动态SQL

目录 1. 环境搭建 &#xff08;1&#xff09;创建持久层接口&#xff0c;并在接口方法上定义Sql语句 &#xff08;2&#xff09;测试方法 &#xff08;3&#xff09;运行结果 2. 注解实现增删查改 &#xff08;1&#xff09;增加用户 &#xff08;2&#xff09;删除用…

【4.17】贪心算法入门

什么是贪心&#xff1f; 贪心的本质是选择每一阶段的局部最优&#xff0c;从而达到全局最优。 刷题或者面试的时候&#xff0c;手动模拟一下感觉可以局部最优推出整体最优&#xff0c;而且想不到反例&#xff0c;那么就试一试贪心。 贪心的解题步骤&#xff1f; 贪心算法一…

《人体地图》笔记

《人体地图》 坂井建雄 著 孙浩 译 腹部通向大腿的隧道 腹部与大腿的分界点是大腿根部&#xff0c;即是腹股沟。 腹壁肌肉连结在腹股沟韧带上&#xff0c;腹壁肌肉包括三层&#xff0c;分别为腹外斜肌、腹内斜肌和腹横肌&#xff0c;每块肌肉都有一个张开的小孔&#xff0c;…