深入JVM:全面解析GC调优

news2025/1/21 9:29:59

文章目录

  • 深入JVM:全面解析GC调优
    • 一、序言
    • 二、GC调优指标
    • 三、GC在线监控
      • 1、Jstat工具
      • 2、VisualVM工具
    • 四、GC日志分析
      • 1、收集GC日志
      • 2、GCViewer工具
      • 3、GCeasy工具
    • 五、GC问题调优
      • 1、调整JVM内存大小
        • (1)调整堆内存大小及比例
        • (2)调整元空间内存大小
        • (3)调整Java虚拟机栈大小
      • 2、择优垃圾回收器
        • (1)模拟环境
        • (2)不同垃圾回收器对比
      • 3、优化垃圾回收器参数
    • 六、后记


深入JVM:全面解析GC调优

一、序言

对于Java工程师而言,深入理解JVM(Java虚拟机)不仅是掌握Java程序运行机制的基础,也是提升系统性能、优化应用和解决复杂问题能力的重要一步,更是Java进阶之路的重中之重。

本文小豪将带大家学习GC调优,了解常见的GC监控和日志分析工具,通过一些应用案例,辅助大家理解GC调优的主要思路。

二、GC调优指标

本篇探讨的JVM知识体系中的GC调优相对关键且较复杂,JVM基础不牢的小伙伴,强烈建议先复习小豪之前发布的内容,以便在阅读本篇时,更容易理解内容。

  1. 【深入JVM:详解JVM内存模型及其演变过程】
  2. 【深入JVM:详解垃圾判定与垃圾回收算法】
  3. 【深入JVM:详解G1垃圾回收器原理】

GC调优主要是针对垃圾回收的性能优化,减少产生Full GC的频率和次数

通常来说,GC性能的评判指标包含三个方面:

  • 吞吐量

    吞吐量是指CPU执行用户代码的时间占总运行时间的比例

    吞吐量 = 执行用户代码时间 /(执行用户代码时间 + GC时间)

  • 暂停时间

    暂停时间指垃圾回收时,用户应用线程产生的暂停时间

  • 内存占用

    内存占用指Java应用所占用的系统内存大小

高吞吐量、低暂停时间和内存占用少共同构成GC性能的衡量指标,但鱼与熊掌不可兼得,这三者往往不能同时满足,需要根据业务场景做相应的折中或取舍。

其中内存占用受限于服务器自身系统内存大小,在日常的调优过程中,我们更为关注另外两个指标:高吞吐量和低暂停时间

高吞吐量以程序的吞吐量为优先,CPU用来更多的执行用户业务线程,往往用于大数据处理或对用户响应时间要求没那么高的后台任务,可能会导致单次垃圾回收的STW时间过长

低暂停时间则以程序的最小暂停时间为优先,用于互联网等对用户响应时间要求比较高的应用中,最大程度减少单次STW时间,但往往垃圾回收器会提前进行垃圾回收,频率高,增加GC线程的工作时间,吞吐量会降低

三、GC在线监控

为发现并定位GC问题,我们首要做的就是监控GC的运行统计信息。小豪这里介绍两款比较常见的工具:Jstat和VisualVM,均可以用于监控GC

本文着重讲解GC调优,对工具类的使用仅作简要说明,详细用法大家可自行学习

1、Jstat工具

Jstat是JDK自带的轻量命令行工具,可以用来在线监控GC信息,无需安装,功能也比较简单,具体用法如下:

jstat [-命令选项] [vmid] [间隔时间/毫秒] [查询次数]

举个例子:

  1. 首先通过jps命令获取本机运行的Java进程ID
  2. 使用jstat来监控某个Java进程的GC信息
jstat -gc 8012 5000

此命令代表每隔5000毫秒输出一次Java进程号8012的GC数据。

在这里插入图片描述

输出的GC数据以表格的形式呈现,比较重要的最后五列代表的内容分别是:

  • YGC、YGCT:年轻代垃圾回收次数和总耗时(单位:秒)
  • FGC、FGCT:老年代(Full GC)垃圾回收次数和总耗时(单位:秒)
  • GCT:总垃圾回收耗时(单位:秒)

显然通过Jstat命令能够简要观察到程序的GC统计信息,包括各个内存区域的使用情况,GC的频率和耗时,但仍不够直观,也无法准确捕获GC产生的时间。

2、VisualVM工具

VisualVM相较于Jstat就比较强大了,同样也是JDK自带的监控工具,能够可视化的在线监控线程,内存使用情况,GC信息等

VisualVM位于Jdk的安装目录的bin目录下(JDK 9及以后需单独下载),双击打开:

在这里插入图片描述

选择需监控的应用程序后,VisualVM右侧展示相应的信息,我们着重关注里面的监视Visual GC状态栏:

  • 监视:展示当前Java程序的CPU、堆内存、类和线程的运行信息,同时能够快速手动触发垃圾回收以及Dump导出栈堆信息

在这里插入图片描述

  • Visual GC:展示当前Java程序堆内存结构、使用情况和GC统计信息,包括GC的次数和耗时

在这里插入图片描述

VisualVM工具默认不带Visual GC插件,这里需在工具栏手动安装下载Visual GC插件:

在这里插入图片描述

同时IDEA也能够集成VisualVM工具,在Plugins插件中搜索VisualVM Launcher进行安装,随后选用VisualVM图标启动程序,VisualVM监控工具则会自动弹出:

在这里插入图片描述

VisualVM总体功能还是比较强大的,支持安装额外的插件,同时可视化界面也更友好,日常使用频率较多,也更适合用于开发环境。

四、GC日志分析

上面介绍的两款工具主要用于在线监控GC信息,初步排查程序是否存在性能问题。

在发现GC问题后,我们需更进一步的了解整个垃圾回收的详细数据,因此需在程序运行过程中收集其产生的GC日志,以便于诊断问题。

1、收集GC日志

开启GC日志信息收集,我们需要在JVM启动命令中设定如下参数:

// 打印详细的GC垃圾回收日志信息
-XX:+PrintGCDetails

// 将GC垃圾回收日志输出到指定的文件中
-Xloggc:文件名

在JDK 9及之后版本中,已将上述命令整合为一个,可直接指定GC日志输出至文件

-Xlog:gc*:file=文件名

输出至文件内容如图:

在这里插入图片描述

GC日志中详细记录整个GC的发生过程,堆内存的整理变化、暂停时间等,但这部分内容看起来比较费力,往往会借助外部工具。这里介绍两种常见的GC日志分析工具:GCViewerGCeasy

2、GCViewer工具

GCViewer是免费的GC日志分析工具,能够将GC日志转换为可视化的图表。也是用Java编写的,使用也比较简单,运行jar包即可。

首先在GitHub官网下载GCViewer源码(地址在这),在本地maven编译打包,生成gcviewer-1.36.jar,启动时加上日志文件即可:

java -jar gcviewer-1.36.jar 日志文件名

启动后界面如下:

在这里插入图片描述

界面左侧Chart图表显示元素过多,这里可以选择View工具栏,勾选掉不关注的曲线:

在这里插入图片描述

界面右侧显示一些统计信息,Summary、Memory、Pause。

Summary中展示程序GC累计暂停时间吞吐量等:

在这里插入图片描述

Pause中展示程序平均暂停时间最小/最大暂停时间等:

在这里插入图片描述

上面列举的这些参数对我们后续GC调优具有指导意义,其它功能大家可自行研究一下。

3、GCeasy工具

GCeasy也是目前比较流行的GC日志分析工具。官网介绍其是业内首款借助机器学习技术引导的垃圾回收日志分析工具,GCeasy内置有智能功能,可自动检测JVM和Android GC日志中的问题并推荐解决方案(官网在这)

在这里插入图片描述

使用也比较简单,只需要登录后将GC日志上传上去即可,GCeasy会自动分析诊断,但每月只能免费使用5次,超过就需要收费了,小豪这里也只是简单了解了一下,使用不多。

五、GC问题调优

上面介绍的GC在线监控工具和GC日志分析工具旨在帮我们发现并诊断GC存在的问题,最终还是需要落实到解决方案上。

在本文开篇我们提到,GC调优主要是针对垃圾回收的性能优化,减少产生Full GC的频率和次数。因此我们进行GC调优核心目标就是减少GC导致的暂停时间和提高程序运行吞吐量,优化我们可以从这几个方面入手:

  1. 调整JVM内存大小:除内存泄漏等问题,调整JVM内存大小往往能解决绝大多数GC性能问题,过小的内存以及不合适的参数大小都会导致频繁发生GC,影响程序性能
  2. 择优垃圾回收器:JVM目前有众多的垃圾回收器,有专注于减少系统暂停时间的CMS,也有专注于提升系统的吞吐量的Parallel Scavenge + Parallel Old组合,也有更为强悍的G1,可以根据不同的业务需求,选择更合适的垃圾回收器
  3. 优化垃圾回收器参数:同样垃圾回收器参数的合理设置也能有效优化GC,这部分就需要针对专门的垃圾回收器进行不断调试和测试

1、调整JVM内存大小

遇事不决加内存!

可能大家经常性遇到Java程序卡顿,就嚷嚷着加内存,没错,加内存百分之99%的情况下确实能解决性能问题。

剩下1%是因为加的还不够多

但更多的是,我们能在固定的内存大小下优化JVM参数,调整内存比例。

(1)调整堆内存大小及比例

调整堆内存大小会用到两个参数:

// (推荐)初始堆内存大小
–Xms[n]

// (推荐)最大堆内存大小
-Xmx[n]

一般来说,我们首先可以考虑在有限的资源条件下扩大堆内存,毕竟堆内存越大Full GC发生频率越小,同时建议-Xms-Xmx设置的大小比例一致,避免堆内存自己动态地调整大小,向操作系统申请内存,浪费性能。

当然我们在设置堆内存大小时也要考虑服务器上其它软件和操作系统自身占用的内存,不能盲目的扩大,例如现在服务器内存为8G,操作系统和其它软件等共占用3G,那么-Xms-Xmx可设置为4G

另外,也可以调整堆中各区域的大小及比例等:

// (不推荐)年轻代的大小,默认总堆的1/3
-Xmn[n]

// (不推荐)年轻代和老年代的大小比例,默认1:2,设置-Xmn后则不用设置此参数
-XX:NewRatio=n

// (不推荐)Survivor区和Eden区大小比例,默认8:1:1
-XX:SurvivorRatio=n

但其实完全不建议这么做,JDK 8默认的Parallel Scavenge和JDK 9及之后默认的G1垃圾回收器,都会动态的调整内存大小,包括年轻代的比例、老年代大小等细节参数。

(2)调整元空间内存大小

调整元空间大小会用到两个参数:

// (推荐)指定元空间最大内存大小
-XX:MaxMetaspaceSize=n
    
// (不推荐)元空间扩容时触发Full GC的阈值,默认约20M
-XX:MetaspaceSize=n

JDK 8及之后版本,元空间使用操作系统本地内存,默认比较大,一般来说我们用不到那么大,有点浪费资源,我们可以控制一下最大值,但也不能配置的过小,避免频繁Full GC,或导致OOM,这里建议在程序运行一段时间后通过Jstat或VisualVM工具查看一下元空间的内存使用情况,确保配置的值大于它。

同时也可以指定当元空间大小使用到-XX:MetaspaceSize配置的阈值后,触发Full GC,默认当元空间内存使用到约20M时会自动触发一次,很多人提到将此参数与元空间最大内存大小保持一致,但其实不建议这样设置,这样做元空间无法触发Full GC,垃圾对象不会被回收。

(3)调整Java虚拟机栈大小

调整Java虚拟机栈大小会用到下列参数:

// (推荐)指定Java虚拟机栈大小
-Xss[n]

Java虚拟机栈大小默认由操作系统决定,一般为1MB2MB,但其实大多数时间压根使用不到,可以将此参数调小一些,在256KB1MB之间即可。

2、择优垃圾回收器

随着JDK版本的迭代,垃圾回收器也在不断优化,垃圾回收器从单线程到多线程、从并行到并发,选择更优的垃圾回收器提升性能。

我们使用GCViewer工具对比一下上一篇中介绍到的几种垃圾回收器性能,分别是:

  • ParNew + CMS
  • Parallel Scavenge + Parallel Old
  • G1
(1)模拟环境

首先编写一个对外接口,这里使用软引用模拟缓存,当每次调用该接口时,默认往内存中存放10M数据,当内存不足时会触发Full GC释放软引用对象:

@RestController
@RequestMapping("/fullGcTest")
public class FullGcController {

    public static Map<String, SoftReference> map;

    // 简略模拟软引用集合,当内存不足时会释放软引用对象
    static {
        map = new HashMap<>();
    }

    @GetMapping("/addMemory")
    public void addMemory() {
        // 随机ID
        String autoId = UUID.randomUUID().toString();
        // 创建软引用对象,占用的内存大小为10M
        SoftReference memory = new SoftReference(new byte[1024 * 1024 * 10]);
        map.put(autoId, memory);
    }
    
}

同时使用Postman中Runner功能模拟高并发下用户访问程序,每次模拟100个用户同时请求,共模拟8轮,累计调用800次该接口。

在这里插入图片描述

在对比过程中,控制其它变量,程序统一运行约1分40秒,堆内存大小等保持一致:

-Xms1g -Xmx1g -Xss256k -XX:MaxMetaspaceSize=256m -XX:+PrintGCDetails -Xloggc:C:\Users\12748\Desktop\log\gclog.txt
(2)不同垃圾回收器对比
  • ParNew + CMS

启用参数:

-XX:+UseParNewGC -XX:+UseConcMarkSweepGC

并发请求8轮次后,GCViewer工具分析ParNew + CMS垃圾回收器的GC日志结果如下:

在这里插入图片描述

吞吐量:95.51%

平均暂停时间:0.04834s

最小暂停时间:0.00049s

最大暂停时间:0.25087s

  • Parallel Scavenge + Parallel Old

PS + PO为JDK 8默认垃圾回收器,不需要在启动参数中指定。

在同样的运行时间和并发请求数下,PS + PO分析结果如下:

在这里插入图片描述

吞吐量:96.22%

平均暂停时间:0.09947s

最小暂停时间:0.01395s

最大暂停时间:0.27486s

可以看出,PS + PO吞吐量高于ParNew + CMS,但平均暂停时间、最小/最大暂停时间均长于ParNew + CMS,也进一步证实了其专注于程序的高吞吐量,更适合于处理大数据或后台任务的应用场景中。反之ParNew + CMS专注于减少系统暂停时间,更适合用于对用户响应时间要求较高的互联网系统中。

  • G1

启用参数(JDK 9及之后版本为默认垃圾回收器):

-XX:+UseG1GC

同样在相同控制变量条件下,G1分析结果如下:

在这里插入图片描述

吞吐量:98.95%

平均暂停时间:0.01446s

最小暂停时间:0.00033s

最大暂停时间:0.04514s

结果很明显,即使G1在JDK 8中还不够成熟(JDK 8之后做了大量优化),但G1其吞吐量、平均暂停时间、最小/最大暂停时间等指标均优于ParNew + CMS和PS + PO。在JDK 8最新版本和JDK 9之后版本,在能选用G1垃圾回收器的情况下,都建议直接使用G1

3、优化垃圾回收器参数

最后,在我们选用合适的垃圾回收器之后,也可以根据垃圾回收器的特点,针对性的进行参数调节。

例如在我们选用G1垃圾回收器之后,合理配置其定制化的参数来优化性能,G1调优的常用参数有:

// 指定Region的内存大小,n必须是2的指数幂,其取值范围是从1M到32M
-XX:G1HeapRegionSize=n

// 设置最大暂停时间,默认200ms
-XX:MaxGCPauseMillis=n

// 指定垃圾回收工作的线程数量
-XX:ParallelGCThreads=n

// 保留空闲的内存区域,默认10%
-XX:G1ReservePercent=n

大多数情况下,这些参数的默认值是经过其设计人员验证下的最优解,因此在配置这些参数后,我们也需要进行大量的测试工作,确认是否对程序性能有改善,否则结果很有可能会适得其反。

更多细节的调优参数,大家也可自行查阅【Oracle官方调优文档】

六、后记

本文从GC调优的指标开始介绍,带领大家认识常用的GC监控和GC日志分析工具,最后讲解了进行GC调优的三个方面,过程中也通过GCViewer分析工具对比了不同垃圾回收器的性能,相信大家对GC调优也有了一定的认知。

当然大多数情况下是不需要进行调优的,一般内存出现问题,更多的可能是代码逻辑问题造成的内存泄露。当然程序的并发访问量较高也会导致频繁GC,那可能更多的就是引入中间件了,进行GC调优往往是最后的解决方案了,同时也随着垃圾回收器的不断改进,近期Azul System公司新推出的C4垃圾回收器几乎可以实现零暂停时间了,应用于我们工作中的GC调优占比会更小,但GC调优作为高阶程序员的必备技能,还是很有必要掌握的。

下一篇,小豪将会更新排查并定位内存泄漏的相关文章,如果大家觉得内容还不错,可以先点点关注,共同进步~

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

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

相关文章

从头搭hadoop集群--分布式hadoop集群搭建

模板虚拟机安装配置见博文&#xff1a;https://blog.csdn.net/weixin_66158110/article/details/139236148 配置文件信息如下&#xff1a;https://pan.baidu.com/s/1074eD5aNVugEPcjwVvi9jA?pwdl1xq&#xff08;提取码&#xff1a;l1xq&#xff09; hadoop版本&#xff1a;h…

行车记录仪人体感应雷达开关模块,飞睿智能雷达模块穿透玻璃、告别漏触烦恼,安防停车监控新方案

随着汽车保有量的持续增长&#xff0c;行车记录仪作为汽车安全配件的必备品&#xff0c;其重要性日益凸显。然而&#xff0c;传统的行车记录仪传感器在停车时往往存在无法穿透玻璃、漏触等问题&#xff0c;给车主带来了诸多不便和安全隐患。本文将深入探讨停车场景下&#xff0…

2024年清洁能源与可持续发展国际会议(ICDESMF 2024)

2024 International Conference on Clean Energy and Sustainable Development 【1】大会信息 会议简称&#xff1a;ICDESMF 2024 大会时间&#xff1a;2024-07-22 大会地点&#xff1a;中国大理 截稿时间&#xff1a;2024-07-08(以官网为准&#xff09; 审稿通知&#xff1a…

【云原生】Kubernetes----POD控制器

目录 引言 一、Pod控制器概述 二、Pod控制器的种类 &#xff08;一&#xff09;ReplicaSet &#xff08;二&#xff09;Deployment &#xff08;三&#xff09;StatefulSet &#xff08;四&#xff09;DaemonSet &#xff08;五&#xff09;Job 三、使用POD控制器 &a…

五、nodejs存储图片

nodejs存储图片 // 静态托管和数据库创建 创建数据库 新建Public进行静态托管 新建个img的文件夹 在index.js里 // 托管静态 app.use(/public, express.static(./Public))//托管静态资源 /*** 1.引入一个express框架* 2.在加载所有服务模块前&#xff0c;要先连接数据库* …

MySQL是怎么保证持久性的(redo log日志相关)

Mysql中 事务的很多实现&#xff0c;都是因为有日志的支撑&#xff0c;比如binlog、undo log、redo log等 MySQL是怎么保证持久性的 持久性是指&#xff0c;事务一旦提交&#xff0c;它对数据库的改变就应该是永久性的&#xff0c;接下来的其他操作或故障不能对其有影响。In…

Java(十)——内部类

文章目录 内部类静态内部类实例内部类匿名内部类局部内部类 内部类 Java内部类是一种特殊的类定义方式&#xff0c;它允许在一个类的内部定义另一个类。 内部类可以访问其所在外部类的成员变量和成员方法&#xff0c;这使得它非常适用于封装与外部类紧密相关的私有逻辑。 内…

如何处理SSL证书过期问题?

SSL证书是网络安全的重要组成部分&#xff0c;它为网站提供了数据加密、身份验证和增强用户信任等多重保护。然而&#xff0c;SSL证书并非永久有效&#xff0c;其有效期通常为一年。当SSL证书过期时&#xff0c;网站安全性会受到影响&#xff0c;甚至可能面临安全风险。本文旨在…

实战|基于YOLOv10与MobileSAM实现目标检测与分割【附完整源码】

《博主简介》 小伙伴们好&#xff0c;我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。 ✌更多学习资源&#xff0c;可关注公-仲-hao:【阿旭算法与机器学习】&#xff0c;共同学习交流~ &#x1f44d;感谢小伙伴们点赞、关注&#xff01; 《------往期经典推…

开发一个comfyui的自定义节点-支持输入中文prompt

文章目录 目标功能开发环境实现过程翻译中文CLIP编码拓展仓库地址完整代码目标功能 目前comfyui的prompt提示词输入节点 CLIP Text Encode 只支持输入英文的prompt,而有时候我们需要自己制定一些prompt,所以就得将我们想要的提示词翻译为英文后再复制粘贴到该节点的输入框中…

PMP考试难吗?考试通过率有多少?

我们通常以考试的通过率来评判一个考试的难易程度。通常通过率达到60%以上&#xff0c;这个考试就不太难&#xff1b;达到80% &#xff0c;这个考试就是不难的。 PMP考试难吗&#xff1f; 不少想要考PMP的小伙伴都会有这样的疑惑&#xff0c;首先以PMP的含金量来说&#xff0…

基于Dify的QA数据集构建(附代码)

大模型相关目录 大模型&#xff0c;包括部署微调prompt/Agent应用开发、知识库增强、数据库增强、知识图谱增强、自然语言处理、多模态等大模型应用开发内容 从0起步&#xff0c;扬帆起航。 大模型应用向开发路径&#xff1a;AI代理工作流大模型应用开发实用开源项目汇总大模…

python 内置map()函数(高效处理序列数据方法,将函数应用于一个序列的每个元素)(懒加载)

文章目录 深入解析 Python 内置函数 map()函数定义与用法基本示例 map() 与列表推导式比较&#xff08;列表推导式在语法上更加简洁&#xff0c; map() 在某些情况下执行效率更高&#xff09;示例&#xff1a;将数字转化为字符串 map() 结合 lambda 函数使用多个序列结论 深入解…

kubernetes负载均衡---MetalLB

https://github.com/metallb/metallb 参考 &#xff1a; https://mp.weixin.qq.com/s/MBOWfcTjFMmgJFWw-FIk0Q 自建的Kubernetes集群&#xff0c;默认情况下是不支持负载均衡的。当需要提供服务的外部访问时&#xff0c;可使用 Ingress、NodePort等方式。他们都存在一些问题 …

智能楼宇安防3D数据可视化平台满足日益增长的安防需求

在当今社会&#xff0c;安全是每个人和企业最为关心的问题。为满足日益增长的安防需求&#xff0c;3D可视化公司深圳华锐视点隆重推出安防平台3D可视化管理系统&#xff0c;以先进的三维技术为您的安全保驾护航。 安防平台3D可视化管理系统通过创新的三维可视化技术&#xff0c…

计算机毕业设计项目、管理系统、可视化大屏、大数据分析、协同过滤、推荐系统、SSM、SpringBoot、Spring、Mybatis、小程序项目编号1-500

大家好&#xff0c;我是DeBug&#xff0c;很高兴你能来阅读&#xff01;作为一名热爱编程的程序员&#xff0c;我希望通过这些教学笔记与大家分享我的编程经验和知识。在这里&#xff0c;我将会结合实际项目经验&#xff0c;分享编程技巧、最佳实践以及解决问题的方法。无论你是…

CCIG学术论坛|文档解析技术加速大模型训练与应用

目录 前言一、大模型训练和应用过程的关键环节面临的问题1、数据2、算力3、语料4、训练时间5、模型规模与复杂度6、部署和推理效率7、安全和隐私 二、高精准、高效率的文档解析三、文档解析技术难点四、TextIn文档解析1、算法Pipeline2、文档图像预处理算法效果3、版面分析算法…

平等未来的契机?探索通用人工智能AGI对社会平等的影响

打破知识壁垒&#xff1a;通用人工智能AGI如何助力社会平等与个人成长 ©作者|CodeDan 来源|神州问学 一&#xff0e; AGI是什么&#xff1f; AGI全称为Artificial General Intelligence&#xff0c;中文翻译过来为通用人工智能。它指的是至少在大多数任务上能与人类媲美…

开源模型应用落地-LangChain试炼-LCEL-表达式语言(一)

一、前言 尽管现在的大语言模型已经非常强大&#xff0c;可以解决许多问题&#xff0c;但在处理复杂情况时&#xff0c;仍然需要进行多个步骤或整合不同的流程才能达到最终的目标。然而&#xff0c;现在可以利用langchain来使得模型的应用变得更加直接和简单。 LCEL是什么&…

为什么说PMP考试比较简单?

PMP考试&#xff0c;说实话&#xff0c;真不算难。不信&#xff1f;听我慢慢给你分析。 PMP考试虽然考察的知识面宽&#xff0c;但很多知识的深度其实并不深。 项目经理嘛&#xff0c;就像是个通才&#xff0c;啥都得懂点&#xff0c;但啥也不用精通到专家级别。就像那概率统…