第三章:JVM监控及诊断工具-GUI篇

news2024/12/23 5:28:03

JVM监控及诊断工具-GUI篇

使用上一章命令行工具或组合能帮您获取目标Java应用性能相关的基础信息,但它们存在下列局限:

  • 无法获取方法级别的分析数据,如方法间的调用关系、各方法的调用次数和调用时间等(这对定位应用性能瓶颈至关重要)。
  • 要求用户登录到目标Java应用所在的宿主机上,使用起来不是很方便。
  • 分析数据通过终端输出,结果展示不够直观。

为此,JDK提供了一些内存泄漏的分析工具, 如jconsole, jvisualvm等, 用于辅助开发人员定位问题,但是这些工具很多时候并不足以满足快速定位的需求。所以这里我们介绍的工具相对多一些、丰富一些。

JDK自带的

  • jconsole: JDK自带的可视化监控工具。 查看Java应用程序的运行概况、监控堆信息、永久区(或元空间)使用情况、类加载情况等位置: jdk\bin\jconsole. exe

  • Visual VM:Visual VM是一个工具,它提供了一个可视界面,用于查看Java虚拟机上运行的基于Java技术的应用程序的详细信息。位置: jdk\bin\jvisualvm. exe

  • JMC:Java Mission Control,I内置Java Flight Recorder。能够以极低的性能开销收集Java虛拟机的性能数据。

第三方工具

  • MAT: MAT (Memory Analyzer Tool) 是基于Eclipse的内存分析工具,是一个快速、功能丰富的Java heap分析工具,它可以帮助我们查找内存泄漏和减少内存消耗Eclipse的插件形式
  • JProfiler:商业软件,需要付费。功能强大。与VisualVM类似
  • Arthas:Alibaba开源的Java诊断工具。 深受开发者喜爱。
  • Btrace:Java运行时追踪工具。可以在不停机的情况下,跟踪指定的方法调用、构造函数调用和系统内存等信息。

JConsole

cmd命令行直接输入 JConsole

或者

在 jdk 的bin 目录下点击 JConsole.exe

image-20221030191348407

Visual VM

  • VisualVM是一个功能强大的多合一故障诊断和性能监控的可视化工具。
  • 它集成了多个JDK命令行工具,使用Visual VM可用于显示虚拟机进程及进程的配置和环境信息(jps,jinfo),监视应用程序的CPU、GC、堆、方法区及线程的信息(jstat、jstack)等,甚至代替JConsole。

在JDK 6 Update 7以后,Visual VM便作为JDK的一部分发布(VisualVM在JDK/bin目录下) 即:它完全免费。

此外,Visual VM也可以作为独立的软件安装:
首页: https://visualvm.github.io/index.html

启动方式

  1. cmd 命令行使用 jvisualvm 命令启动

  2. 在IDEA 安装 jvisualvm 插件 启动

    • 安装好后,设置 jvisualvm 路径

    image-20221030193053046

连接方式

image-20221030192835664

基本功能

生成 dump 文件

  1. 点击进程右键 —— 生成 dump

image-20221030194011991

  1. 保存 dump 文件

image-20221030194045032

生成线程 dump 文件

在程序中出现死锁等情况,也会进行提示。

image-20221030194813036

生成的 dump 文件显示线程信息

image-20221030194903732

MAT

MAT可以分析heap dump文件。 在进行内存分析时,只要获得了反映当前设备内存映像的hprof文件,通过MAT打开就可以直观地看到当前的内存信息。、

一般说来,这些内存信息包含:

  • 所有的对象信息,包括对象实例、成员变量、存储于栈中的基本类型值和存储于堆中的其他对象的引用值。

  • 所有的类信息,包括classloader、类名称、父类、静态变量等

  • GCRoot到所有的这些对象的引用路径

  • 线程信息,包括线程的调用栈及此线程的线程局部变量(TLS)

说明1
MAT不是一个万能工具,它并不能处理所有类型的堆存储文件。但是比较主流的厂家和格式,例如 Sun,HP,SAP所采用的HPROF二进制堆存储文件,以及IBM的PHD堆存储文件等都能被很好的解析。

说明2:
最吸引人的还是能够快速为开发人员生成内存泄漏报表,方便定位问题和分析问题。虽然MAT有如此强大的功能,但是内存分析也没有简单到一键完成的程度,很多内存问题还是需要我们从MAT展现给我们的信息当中通过经验和直觉来判断才能发现。

生成 dump 的四种方式

  • 方法一: 通过前一章介绍的jmap工具生成,可以生成任意-个java进程的dump文件;
  • 方法二: 通过配置JVM参数生成。
    • 选项"-XX: +HeapDumpOnOutOfMemoryError" 或”-XX: +HeapDumpBeforeFullGC"
    • 选项" -XX :HeapDumpPath"所代表的含义就是当程序出现0utofMemory时,将会在相应的目录下生成一份dump文件。如果不指定选项“-XX :HeapDumpPath”则在当前目录下生成dump文件。

对比:考虑到生产环境中几乎不可能在线对其进行分析,大都是采用离线分析,因此使用 jmap+MAT工
具是最常见的组合。

  • 方法三: 使用VisualVM可以 导出堆dump文件
  • 方法四: 使用MAT既可以打开一个已有的堆快照,也可以通过MAT直接从活动Java程序中导出堆快照。该功能将借助jps列出当前正在运行的Java 进程,以供选择并获取快照。

Histogram

可以看到程序中所有的类,以及所占用内存大小。

左侧有类的详细信息。

image-20221030214130962

浅堆和深堆

浅堆(Shallow Heap)是指一个对象所消耗的内存。在32位系统中,一个对象引用会占据4个字节,一个int类型会占据4个字节,long型变量会占据8个字节,每个对象头需要占用8个字节。根据堆快照格式不同,对象的大小可能会向8字节进行对齐。

以String为例: 2个int值共占8字节, 对象引用占用4字节,对象头8字节,合计20字节,向8字节对齐,
故占24字节。(jdk7中)
int hash32
int hash
ref value C:\Users\Administratu

这24字节为String对象的浅堆大小。它与String的value实际取值无关,无论字符串长度如何,浅堆大小始终是24字节。

保留集(Retained Set)
对象A的保留集指当对象A被垃圾回收后,可以被释放的所有的对象集合(包括对象A本身),即对象A的保留集可以被认为是只能通过对象A被直接或间接访问到的所有对象的集合。通俗地说,就是指仅被对象A所持有的对象的集合。

深堆(Retained Heap)
深堆是指对象的保留集中所有的对象的浅堆大小之和。

注意: 浅堆指对象本身占用的内存,不包括其内部引用对象的大小。一个对象的深堆指只能通过该对象访问到的(直接或间接)所有对象的浅堆之和,即对象被回收后,可以释放的真实空间。

实际大小

另外一个常用的概念是对象的实际大小。这里,对象的实际大小定义为一个对象所能触及的所有对象的浅堆大小之和,也就是通常意义上我们说的对象大小。与深堆相比,似乎这个在日常开发中更为直观和被人接受,但实际上,这个概念和垃圾回收无关。

下图显示了一个简单的对象引用关系图,对象A引用了C和D,对象B引用了C和E。那么对象A的浅堆大小只是A本身,不含C和D,而A的实际大小为A、C、D三者之和。而A的深堆大小为A与D之和,由于对象C还可以通过对象B访问到,因此不在对象A的深堆范围内。

image-20221031161045919

浅堆大小是指对象引用本身的大小

实际大小是指浅堆包括它所引用的对象大小之和

深堆大小是指只有浅堆指向的对象大小之和

支配树

支配树的概念源自图论

MAT提供了一个称为支配树(Dominator Tree)的对象图。支配树体现了对象实例间的支配关系。在对象引用图中,所有指向对象B的路径都经过对象A[则认为对象A支配对象B。如果对象A是离对象B最近的一个支配对象,则认为对象A为对象B的直接支配者。支配树是基于对象间的引用图所建立的,它有以下基本性质:

  • 对象A的子树(所有被对象A支配的对象集合)表示对象A的保留集(retained set) ,即深堆。

  • 如果对象A支配对象B,那么对象A的直接支配者也支配对象B。

  • 支配树的边与对象引用图的边不直接对应。

如下图所示: 左图表示对象引用图,右图表示左图所对应的支配树。

对象A和B由根对象直接支配,由于在到对象C的路径中,可以经过A,也可以经过B,因此对象C的直接支配者也是根对象。对象F与对象D相互引用,因为到对象F的所有路径必然经过对象D,因此,对象D是对象F的直接支配者。而到对象D的所有路径中,必然经过对象C,即使是从对象F到对象D的引用,从根节点出发,也是经过对象C的,所以,对象D的直接支配者为对象C。

image-20221031164530175

同理,对象E支配对象G。到达对象H的可以通过对象D,也可以通过对象E,因此对象D和E都不能支配对象H,而经过对象C既可以到达D也可以到达E,因此对象C为对象H的直接支配者。

再谈:内存泄漏

可达性分析算法来判断对象是否是不再使用的对象,本质都是判断一个对象是否还被引用。那么对于这种情况下,由干代码的实现不同就会出现很多种内存泄漏问题(让JVM误以为此对象还在引用中,无法回收,造成内存泄漏)。

内存泄漏(memory leak) 的理解

严格来说,只有对象不会再被程序用到了,但是GC又不能回收他们的情况,才叫内存泄漏。但实际情况很多时候一些不太好的实践(或疏忽)会导致对象的生命周期变得很长甚至导致0OM,也可以叫做宽泛意义上的“内存泄漏”

image-20221031170250795

对象X引用对象Y,X的生命周期比Y的生命周期长;
那么当Y生命周期结束的时候,x依然引用着Y,这时候,垃圾回收期是不会回收对象Y的;
如果对象X还引用着生命周期比较短的A、B、C,对象A又引用着对象a、b、C,这样就可能造成大量无用的对象不能被回收,进而占据了内存资源,造成内存泄漏,直 到内存溢出。

泄漏的分类

  • 经常发生:发生内存泄露的代码会被多次执行,每次执行,泄露一块内存;
  • 偶然发生:在某些特定情况下才会发生;
  • 一次性:发生内存泄露的方法只会执行次;
  • 隐式泄漏:一直占着内存不释放,直到执行结束;严格的说这个不算内存泄漏,因为最终释放掉了,但是如果执行时间特别长,也可能会导致内存耗尽。

内存泄漏的八种情况

1、静态集合类,如HashMap、 LinkedList等等。 如果这些容器为静态的,那么它们的生命周期与JVM程序一致,则容器中的对象在程序结束之前将不能被释放,从而造成内存泄漏。简单而言,长生命周期的对象持有短生命周期对象的引用,尽管短生命周期的对象不再使用,但是因为长生命周期对象持有它的引用而导致不能被回收。

image-20221031172355827

2、单例模式,和静态集合导致内存泄露的原因类似,因为单例的静态特性,它的生命周期和JVM的生命周期一样长,所以如果单例对象如果持有外部对象的引用,那么这个外部对象也不会被回收,那么就会造成内存泄漏。

3、内部类持有外部类,如果一个外部类的实例对象的方法返回了一个内部类的实例对象。这个内部类对象被长期引用了,即使那个外部类实例对象不再被使用,但由于内部类持有外部类的实例对象,这个外部类对象将不会被垃圾回收,这也会造成内存泄漏。

4、各种连接,如数据库连接、网络连接和I0连接等。
在对数据库进行操作的过程中,首先需要建立与数据库的连接,当不再使用时,需要调用close方法来释放与数据库的连接。只有连接被关闭后,垃圾回收器才会回收对应的对象。否则,如果在访问数据库的过程中,对Connection、Statement 或ResultSet不显性地关闭,将会造成大量的对象无法被回收,从而引起内存泄漏。

image-20221031172504276

5、 变量不合理的作用域。-般而言,一个变量的定义的作用范围大于其使用范围,很有可能会造成内存泄漏。另一方面,如果没有及时地把对象设置为nu1l,很有可能导致内存泄漏的发生。

image-20221031172546214

6、改变哈希值

7、缓存泄露

8、监听器和回调

OQL 语句

MAT支持一种类似 于SQL的查询语言OQL (Object Query Language)。OQL使用类SQL语法,可以在堆中进行对象的查找和筛选。

Select子句
在MAT中,Select 子句的格式与SQL基本一致,用于指定要显示的列。Select子句中可以使用“*”,查看结果对象的引用实例(相当于outgoing references) 。

SELECT * FROM java. util. Vector v

使用“OBJECTS"关键字,可以将返回结果集中的项以对象的形式显示。

SELECT objects v. elementData FROM java. util. Vector v
SELECT OBJECTS s.value FROM java.lang.String s

在Select子句中,使用“AS RETAINED SET” 关键字可以得到所得对象的保留集。

SELECT AS RETAINED SET * FROM com. atguigu . mat . Student

“DISTINCT”关键字用于在结果集中去除重复对象。

SELECT DISTINCT OBJECTS classof(s) FROM java.lang.String s

From子句
From子句用于指定查询范围,它可以指定类名、正则表达式或者对象地址。

SELECT * FROM java.lang.String s

下例使用正则表达式,限定搜索范围,输出所有com. atguigu包下所有类的实例

SELECT * FROM ”com.atguigu… *"

也可以直接使用类的地址进行搜索。使用类的地址的好处是可以区分被不同ClassLoader加载的同一种类型。

select * from 0x37 a0b4d

Where子句
Where子句用于指定OQL的查询条件。0QL查询将只返回满足Where子句指定条件的对象。
Where子句的格式与传统SQL极为相似。下例返回长度大于10的char数组。

SELECT * FROM char[] s WHERE s.@length>10

下例返回包含“java”子字符串的所有字符串,使用“LIKE”操作符,“LIKE” 操作符的操作参数为正则表达式。

SELECT * FROM java.lang.String s WHERE toString(s) LIKE “. java.

下例返回所有value域不为nu11的字符串,使用“=”操作符。

SELECT * FROM java.lang.String s where s. value != nu11

Where子句支持多个条件的AND、OR运算。下 例返回数组长度大于15,并且深堆大于1000字节的所有Vector对象。

SELECT * FROM java. util.Vector v WHERE v. elementData. @1ength>15 AN D v . @retainedHeapSize>1000

内置对象和方法

OQL中可以访问堆内对象的属性,也可以访问堆内代理对象的属性。访问堆内对象的属性时,格式如下:
[ . ] . .
其中alias为对象名称。访问java. io. File对象的path属性,并进一步访问path的value属性:

SELECT toString(f .path. value) FROM java.io.File f

下例显示了String对象的内容、objectid和objectAddress

SELECT s.toString(), s.@objectId, s. @objectAddress FROM java.lang.String s

下例显示java. util. Vector内部数组的长度。

SELECT v.elementData. @1ength FROM java. util.Vector v

下例显示了所有的java. util. Vector对象及其子类型

select from INSTANCEOF java. util. Vector

Jprofiler

下载与安装

下载地址:https://plugins.jetbrains.com/plugin/253-jprofiler

在 IDEA 中下载 JProfiler 插件:

image-20221031195434158

配置 JProfiler 的执行文件:

image-20221031195504539

最终出现这俩个图标就 OK 了:

image-20221031195524810

数据采集方式

JProfier数据采集方式分为两种: Sampling( 样本采集)Instrumentation ( 重构模式)

Instrumentation : 这是JProfiler全功能模式。在class加载之 前,JProfier把相关功能代码写入到需要分析的class的bytecode中,对正在运行的jvm有一定影响。

  • 优点:功能强大2T在此设置中,调用堆栈信息是准确的。
  • 缺点:若要分析的class较多,则对应用的性能影响较大, CPU开销可能很高(取决于Filter的控制)。因此使用此模式一般配合Filter使用,只对特定的类或包进行分析。

Sampling:类似于样本统计: 每隔一定时间(5ms )将每个线程栈中方法栈中的信息统计出来。

  • 优点:对CPU的开销非常低,对应用影响小(即使你不配置任何Filter)
  • 缺点:一些数据/特性不能提供(例如:方法的调用次数、执行时间)

注: JProfiler本身没有指出数据的采集类型,这里的采集类型是针对方法调用的采集类型。因为JProfiler的绝大多数核心功能都依赖方法调用采集的数据,所以可以直接认为是JProfiler的数据采集类型。

arthas

Arthas (阿尔萨斯)是Alibaba开源的Java诊断工具, 深受开发者喜爱。在线排查问题,无需重启动态跟踪Java代码;实时监控JVM状态。

官网网站:arthas (aliyun.com)

从文档中下载:

image-20221031215619647

image-20221031215639001

下载解压后,在目录中打开 cmd:

image-20221031215710595

提前启动程序,使用 java -jar 的方式启动,启动后选择对应的 pid 进行监控

image-20221031215356497

基础指令

image-20221101141357726

JVM 相关指令

  • dashboard - 当前系统的实时数据面板
    • -i : 多久打印一次,默认毫秒
    • -n :总共打印几次
  • getstatic - 查看类的静态属性
  • heapdump - dump java heap, 类似 jmap 命令的 heap dump 功能
  • jvm - 查看当前 JVM 的信息
  • logger - 查看和修改 logger
  • mbean - 查看 Mbean 的信息
  • memory - 查看 JVM 的内存信息
  • ognl - 执行 ognl 表达式
  • perfcounter - 查看当前 JVM 的 Perf Counter 信息
  • sysenv - 查看 JVM 的环境变量
  • sysprop - 查看和修改 JVM 的系统属性
  • thread - 查看当前 JVM 的线程堆栈信息
  • vmoption - 查看和修改 JVM 里诊断相关的 option
  • vmtool - 从 jvm 里查询对象,执行 forceGc

Class、ClassLoader 相关指令

  • classloader - 查看 classloader 的继承树,urls,类加载信息,使用 classloader 去 getResource
  • dump - dump 已加载类的 byte code 到特定目录
  • jad - 反编译指定已加载类的源码
  • mc - 内存编译器,内存编译.java文件为.class文件
  • redefine - 加载外部的.class文件,redefine 到 JVM 里
  • retransform - 加载外部的.class文件,retransform 到 JVM 里
  • sc - 查看 JVM 已加载的类信息
  • sm - 查看已加载类的方法信息

monitor/watch/trace 相关指令

  • monitor - 方法执行监控
  • stack - 输出当前方法被调用的调用路径
  • trace - 方法内部调用路径,并输出方法路径上的每个节点上耗时
  • tt - 方法执行数据的时空隧道,记录下指定方法每次调用的入参和返回信息,并能对这些不同的时间下调用进行观测
  • watch - 方法执行数据观测
    //arthas.aliyun.com/doc/sm.html) - 查看已加载类的方法信息


各位彭于晏,如有收获点个赞不过分吧…✌✌✌

Alt

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

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

相关文章

手写一个react,看透react运行机制

适合人群 本文适合0.5~3年的react开发人员的进阶。 讲讲废话: react的源码,的确是比vue的难度要深一些,本文也是针对初中级,本意让博友们了解整个react的执行过程。 写源码之前的必备知识点 JSX 首先我们需要了解什么是JSX。…

Flutter高仿微信-第19篇-支付-我的零钱

Flutter高仿微信系列共59篇,从Flutter客户端、Kotlin客户端、Web服务器、数据库表结构、Xmpp即时通讯服务器、视频通话服务器、腾讯云服务器全面讲解。 详情请查看 效果图: 实现代码: /*** Author : wangning* Email : maoning20080809163.…

Unity DOTS学习 前置知识(一)

DOTS是什么 Data-Oriented Technology Stack(面向数据的技术栈) Unity 使用的5个核心包: The C# job system 提供快速安全的多线程操作The Burst compiler 优化C#代码的编译器,能够编译生成比mono或者L2CPP更快的代码。可以编译Unity中的任何代码Unit…

解决传统难题,WMS系统实现信息数据实时追踪

随着社会经济的发展,传统仓库的存储和做工难以适应当下市场经济的需求。仓库需要进行转型升级,从而适应当下的环境。在仓库的转型升级过程当中,WMS系统是不可或缺的一部分内容。 而WMS系统的应用会从多方面支持仓库的转型升级,其带…

uniapp之最新获取用户昵称以及头像

前言 在uniapp登录时候最开始想的就是手机号登录之后,就获取用户的昵称以及头像,存储起来,登录的时候直接显示在我的页面,最开始使用的是 uniapp官网自带的uni.getUserProfile的方法,就可以获取用户的头像跟昵称&…

Prometheus Operator与kube-prometheus之二-如何监控1.23+ kubeadm集群

简介 系列文章: 标签 - Prometheus - 东风微鸣技术博客 (ewhisper.cn)Prometheus Operator 的上一篇: Prometheus Operator 与 kube-prometheus 之一 - 简介 - 东风微鸣技术博客 (ewhisper.cn) kube-prometheus-stack捆绑了监控Kubernetes 集群所需的Prometheus Operator、Ex…

Web(二)html5基础-表格基本结构

第1关_网页表格的基本概念 第2关_创建简单的表格 本关任务:创建一个两行两列的表格。 相关知识:为了完成本关任务,你需要掌握:1.表格的结构及对应的标签,2.表格标签的属性。 表格的结构及对应的标签。一个基本的表格是…

Netty系列(二):Netty拆包/沾包问题的解决方案

上一篇说到Netty系列(一):Springboot整合Netty,自定义协议实现,本文聊一些拆包/沾包问题。 拆包/沾包问题 TCP是面向字节流的协议,在发送方发送的若干包数据到接收方接收时,这些数据包可能会被…

Linux笔记

一。基础思想 一切皆文件。 两条权限原则: 权限分组原则权限最小原则 su是切换用户,而sudo则是用root权限执行某操作( 普通用户sudo安全) Linux目录 系统只存在一颗文件树、从/开始,所有的文件都挂载在这个节点上。…

JaCoCo增量覆盖率的基本实现原理

什么是增量覆盖率 如图所示,在master分支提交了HelloController,然后从master拉了个新分支test;提交了第1次代码,增加了WorldController;提交了第2次代码,增加了DonController。增量的获取方式有两种&#…

报表工具使用教程-FineReport决策报表导出Plus

前言 通过决策报表导出插件,用户可以将单张决策报表导出为 Excel ,PDF,Word 格式文件。 那么用户如何将决策报表导出为 PPT 或 Image 格式文件呢?如何将多张决策报表合并导出至一个文件呢? 1.实现思路 用户通过安装…

静态时序分析简明教程(七)]端口延迟

端口延迟一、写在前面1.1 快速导航链接二、端口延迟2.1 输入有效2.2 输出有效2.3 set_input_delay2.3.1 -clock clock_name2.3.2 -clock_fall2.3.3 -level_sensitive2.3.4 -rise/fall2.3.5 min/max2.3.6 -add_delay2.3.7 时钟延迟2.4 set_output_delay三、总结一、写在前面 一…

点击化学FAM荧光素:6-FAM-alkyne,FAM alkyne 6-isomer,6-炔基-羧基荧光素

【中文名称】6-炔基-羧基荧光素 【英文名称】 FAM alkyne,6-isomer,6-FAM-alkyne 【CAS】478801-49-9 【分子式】C24H15NO6 【分子量】413.39 【纯度标准】95% 【包装规格】25mg,50mg,100mg 【是否接受定制】可进行定制,定制时间周…

Kubernetes安装可视化界面

安装可视化界面编写配置文件安装kubernetes-dashboard创建访问账号访问可视化界面dashboard是kubernetes官方提供的可视化界面。 https://github.com/kubernetes/dashboard编写配置文件 创建配置文件存放目录并切换到其中: mkdir /usr/local/kubernetes-dashboard…

java面试强基(10)

Exception 和 Error 有什么区别? 在 Java 中,所有的异常都有一个共同的祖先 java.lang 包中的 Throwable 类。Throwable 类有两个重要的子类: Exception :程序本身可以处理的异常,可以通过 catch 来进行捕获。Exception 又可以分为 Checke…

Twitter网红账号营销,一定不能做的事

做社媒运营,我们都会创建一个官方账号与粉丝进行互动沟通,及时通知我们的新活动、产品,也是我们与粉丝建立联系的一个渠道方法。 推特群推王提示,虽然有这么多的好处,但是,也是有很多事项需要注意的&#…

服务器抓包简介

1、微服务服务器上抓包 2、在nginx服务器上抓包 1、服务器安装抓包软件 yum install -y tcpdump 2、服务器抓包命令 tcpdump -i any -s 0 -vvv -w /opt/qqgh.cap port 8080(本服务器该服务的实际ip地址) tcpdump -i eth0 host 10.30.224.170 -w result.…

14.函数的使用

函数的概念 函数是c语言的功能单位,实现一个功能可以封装成一个函数来实现。 定义函数的时候一切以功能为目的,根据功能去定函数的参数和返回值。 函数的分类 1.从定义角度分类(即函数是谁实现的) 库函数(c库实现的…

Fedora怎么设置主菜单快捷键? Fedora快捷键的设置方法

Fedora主菜单可以设置打开快捷键,该怎么设置呢?下面我们就来看看Fedora快捷键的操作方法。 同时按【ALTF2】,输入gnome-terminal,打开终端。 单击右上角的主菜单按钮。 单击【配置文件首选项】。 单击【快捷键】。 单击【显示主菜…

使用DIV+CSS进行网页布局设计【HTML节日介绍网站——二十四节气】

🎉精彩专栏推荐 💭文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 💂 作者主页: 【主页——🚀获取更多优质源码】 🎓 web前端期末大作业: 【📚毕设项目精品实战案例 (10…