只会CRUD?来学学JVM指标监控和性能调优入门实战吧

AI时代工程师能力模型重构:从代码实现到系统架构与问题定义 在软件工程领域,系统架构设计是构建复杂应用的核心基础,它决定了软件的可扩展性、可维护性性能表现。其原理在于通过合理的模块划分、接口定义组件交互,实现关注点分离职责单一。随着AI编程助手低代码平台的普及,代码实现效率大幅提升,技术价值正从单纯的功能实现转向更高维度的系统设计与问题定义。在应用场景上,工程师需要面对微服务架构、云原生部署、高并发处理等复杂工程挑战。本文聚焦于工程师能力模型的变迁,探讨在AI浪潮下,如何通过精准的问题定义、架构设计判断力跨域协同能力,突破职业发展瓶颈,实现从“程序员”到“ 阅读详情

前言

对于JVM监控与调优,部分Java开发还停留在听说过的层面但是并没有实践过的情况,笔者也曾停留在这个阶段,自认为JVM监控与调优不过是书本上的东西,自认为JVM十分稳定,会帮我们处理任何的Java程序问题。诚然,Java发展到如今,JVM已经是一个很成熟且稳定的产品了,但是,再好的武器也要看使用者,新手拿着屠龙刀也杀不死一只小公鸡,对于一些初入编程的小白来说,是有可能写出一些代码让稳定的JVM不再稳定的,所以问了解决这些问题,学好并且用好JVM监控与调优工具是很有必要的一件事情,通过合理的使用工具和分析,我相信我们可以解决百分之九十的JVM问题,至于那百分之十,这钱我们不赚也罢。

一、监控篇

公司的基建一般只能监控整体运行情况,内存占用,GC延迟,是否出现异常等,并没有提供具体的监控(线程、内存、堆栈、GC)。

1.1 基础监控工具

1.1.1 jps:jvm进程监控工具

列出正在运行的虚拟机进程,并显示虚拟机主类。

1.1.2 jstat:查看JVM统计信息

jstat:查看JVM统计信息,注意,它只能监控JVM整体的运行状态,比如堆内存分布,GC收集状态等。

jstat是用于监视虚拟机各种运行状态信息的命令行工具。它可以显示本地或者远程(通过RMI协议)虚拟机进程中的类加载、内存、垃圾收集、即时编译等运行时数据,在没有 GUI图形界面、只提供了纯文本控制台环境的服务器上,它将是运行期定位虚拟机性能问题的常用工具。

表4-2 jstat工具主要选项

不过其实各大厂其实都有监控相关的基建平台,针对JVM的堆内存分布,GC执行情况等统计信息都会有相应的监控和报警。

1.1.3 jinfo:Java配置信息工具

jinfo的作用是实时查看和调整虚拟机各项参数。使用jps命令的 -v参数可以查看虚拟机启动时显式指定的参数列表,但如果想知道未被显式指定的参数的系统默认值,除 了去找资料外,就只能使用jinfo的 -flag 选项进行查询了。

JDK 6之后,jinfo在Windows和Linux平台都有提供,并且加入了在运行期修改部分参数值的能力(可以使用-flag[+|-]name或者-flag name=value在运行期修改一部分运行期可写的虚拟机参数值)

1.1.4 jmap⭐️:Java内存映像工具(解决内存问题)

jmap命令用于生成堆转储快照(一般称为heapdump或dump文件) 。如果不使用jmap 命令,要想获取J ava堆转储快照也还有一些比较“ 暴力”的手段:譬如**- XX:+HeapDumpOnOutOfMemoryError**参数,可以让虚拟机在内存溢出异常出现之后自动生成堆转储快照文件。

jmap 的作用并不仅仅是为了获取堆转储快照,它还可以查询finalize执行队列、Java堆和方法区的详细信息,如空间使用率、当前用的是哪种收集器等。虽然他最大的用途是用来获取HeapDump文件。

生成了heapdump快照文件之后,我们就可以利用一些工具比如jhat、Eclipse MAT、VisualVM等来分析heapdump文件,从而知道生成这一快照的时候的堆内存主要分布在哪些对象上面,从而来解决出现的如堆内存飙升,OOM等问题。

1.1.5 jstack⭐️:Java堆栈跟踪工具(解决线程问题)

jstack命令用于生成虚拟机当前时刻的线程快照(一般称为threaddump或者 javacore文件) 。线程快照就是当前虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的 目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间挂 起等,都是导致线程长时间停顿的常见原因。线程出现停顿时通过jstack来查看各个线程的调用堆栈, 就可以获知没有响应的线程到底在后台做些什么事情,或者等待着什么资源。

比如常见的面试题:如何排查CPU100%?就可以通过top + jstack来分析占用CPU最高的线程的堆栈信息。

比如常见的面试题:如何排查一个请求长时间没有响应?就可以通过jstack来分析该请求线程的堆栈信息。

总结

对于以上提到的基础命令行工具,程序员们应该要熟练掌握,虽然下文提到的可视化故障处理工具囊括了以上工具的所有功能(没错就是这么牛掰),但是可视化工具如果要连接远程的服务器,基本上都是基于一些远程连接协议支持的,但是并不是所有远程服务器都支持的,也许刚好你的项目所部署的服务器不支持一些远程连接协议,那么此时就只能通过命令行工具来进行了,所以掌握以上这些工具对于程序员来说是很有必要的。

1.2 可视化故障处理工具

JDK中除了附带大量的命令行工具外,还提供了几个功能集成度更高的可视化工具,用户可以使用这些可视化工具以更加便捷的方式进行进程故障诊断和调试工作。这类工具主要包括JConsole、 JHSDBVisualVMJMC四个。其中前三个是免费使用,后一个是需要付费的,由于可视化故障处理工具都大差不差,所以本文主要介绍一下VisualVM这个功能最强大的工具。

1.2.1 VisualVM:多功能故障处理工具

VisualVM(All-in-One Java Troubleshooting Tool)是功能最强大的运行监视和故障处理程序之一, 曾 经 在 很 长 一 段 时 间 内 是 Oracle官 方 主 力 发 展 的 虚 拟 机 故 障 处 理 工 具 。Oracle曾 在 VisualVM的 软 件 说 明 中写上了“ All-in-One”的字样,预示着它除了常规的运行监视、故障处理外,还将提供其他方面的能 力,譬如性能分析(Profiling)。VisualVM 的性能分析功能比起JProfiler、YourKit等专业且收费的 Profiling工 具 都 不 遑 多 让 。而 且 相 比 这 些 第 三 方 工 具 , VisualVM还 有 一 个 很 大 的 优 点 : 不 需 要 被 监 视 的 程序基于特殊Agent去运行,因此它的通用性很强,对应用程序实际性能的影响也较小,使得它可以直 接应用在生产环境中。这个优点是JProfiler、YourKit等工具无法与之媲美的。

VisualVM具备丰富的插件拓展功能,通过不同的功能插件, VisualVM可以做到 :

  • 显示虚拟机进程以及进程的配置、环境信息(jp s、jinfo)。

  • 监视应用程序的处理器、垃圾收集、堆、方法区以及线程的信息(jstat、jstack)。

  • dump 以及分析堆转储快照(jmap 、jhat )。

  • 方法级的程序运行性能分析,找出被调用最多、运行时间最长的方法。

  • 离线程序快照:收集程序的运行时配置、线程dump 、内存dump 等信息建立一个快照,可以将快 照发送开发者处进行Bug反馈。

  • 其他插件带来的无限可能性。

(1)启动方式

1.使用JDK自带的工具包,在命令行输入jvisualvm即可启动程序

2.单独下载VisualVM软件工具

(2)插件安装

菜单栏——> 工具 ——> 插件

连接方式

(3)主要功能

visualVm的功能十分丰富,涵盖了我们之前说的所有基础监控工具的功能,并且还有一些自己独有的功能。

他的主要功能如下:

  1. 生成/读取HeapDump文件(堆内存快照) == jmap

  2. 查看JVM参数和系统属性 == jinfo

  3. 查看运行中的虚拟机进程 == jps

  4. 生成/读取线程快照 == jstack

  5. 程序资源的实时监控

  6. 其他功能

    1. JMX远程连接

    2. 远程环境监控

    3. CPU分析和内存分析

真可谓是十分强大了。这里我们重点介绍一下堆内存快照分析和线程快照分析

(4)HeapDump文件生成和分析

在监视页面点击 堆dump 之后,就会生成一份Dump文件。

  • dump文件概述:我们可以在概要信息中看到此时堆内存的统计信息

  • 堆内存分布信息⭐️:通过该信息,我们可以发现哪些类占有大量的内存,从而分析程序的堆内存问题

(5)线程Dump文件生成和分析
  • 在线程面板生成线程Dump文件

  • 查看Dump文件:我们以第一个举例

    • “RMI TCP ....”表示线程名

    • daemon表示是一个后台线程

    • prio表示线程的优先级,用处不大

    • tid:Java虚拟机中的线程ID

    • nid:线程本地标识,是线程在当前操作系统的线程ID标识(和TOP 命令展示的线程/进程ID是同一个)

    • [xxxxxx]:对象的内存地址

    • Java.lang.thread.State:RUNNABLE:显示线程当前时刻的状态,括号里面是处于该状态的原因。

    • 线程堆栈:最下面以at开头的是线程的调用堆栈信息,通过它可以看出线程执行代码的流程,以及该线程目前在哪个代码块阻塞了

二、案例篇

2.1 内存泄露

可达性分析算法来判断对象是否是不再使用的对象,本质都是判断一个对象是否还被引用。那么对于这种情况下,由于代码的实现不同就会出现很多种内存泄漏问题,当出现以下两个场景的时候,就代表发生了内存泄漏。

  • 是否还被引用?是

  • 是否还被需要?否

申请了内存用完了不释放,比如一共有 1024M 的内存,分配了 512M 的内存一直不回收,那么可以用的内存只有 512M 了,仿佛泄露掉了一部分;通俗一点讲的话,内存泄漏就是【占着茅坑不拉 shi】

2.2 几种情况

下面介绍一下可能会出现内存泄漏的几种情况,同学们在进行相关程序开发的时候,要格外注意以下这些情况,防止出现内存泄漏导致程序内存问题。

(1)静态集合类

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

public class MemoryLeak {
    static List list = new ArrayList();
    public void oomTests(){
        Object obj=new Object();//局部变量
        list.add(obj);
    }
}
(2)单例模式

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

(3)内部类持有外部类

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

最佳实践:内部类最好只在内部使用。

(4)各种连接,如数据库连接、网络连接和 IO 连接等

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

public static void main(String[] args) {
    try{
        Connection conn =null;
        Class.forName("com.mysql.jdbc.Driver");
        conn =DriverManager.getConnection("url","","");
        Statement stmt =conn.createStatement();
        ResultSet rs =stmt.executeQuery("....");
    } catch(Exception e){//异常日志
    } finally {
        // 1.关闭结果集 Statement
        // 2.关闭声明的对象 ResultSet
        // 3.关闭连接 Connection
    }
}
(5)变量不合理的作用域

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

public class UsingRandom {
    private String msg;
    public void receiveMsg(){
        readFromNet();//从网络中接受数据保存到msg中
        saveDB();//把msg保存到数据库中
    }
}

如上面这个伪代码,通过 readFromNet 方法把接受的消息保存在变量 msg 中,然后调用 saveDB 方法把 msg 的内容保存到数据库中,此时 msg 已经就没用了,由于 msg 的生命周期与对象的生命周期相同,此时 msg 还不能回收,因此造成了内存泄漏。实际上这个 msg 变量应该放在 receiveMsg 方法内部,当方法使用完,那么 msg 的生命周期也就结束,此时就可以回收了。还有一种方法,在使用完 msg 后,把 msg 设置为 null,这样垃圾回收器也会回收 msg 的内存空间。

(6)改变哈希值

改变哈希值,当一个对象被存储进 HashSet 集合中以后,就不能修改这个对象中的那些参与计算哈希值的字段了。

否则,对象修改后的哈希值与最初存储进 HashSet 集合中时的哈希值就不同了,在这种情况下,即使在 contains 方法使用该对象的当前引用作为的参数去 HashSet 集合中检索对象,也将返回找不到对象的结果,这也会导致无法从 HashSet 集合中单独删除当前对象,造成内存泄漏。

这也是 String 为什么经常被当作HashMap的Key的原因,由于String不可变,我们可以放心地把 String 当做 HashMap 的 key 值;

当我们想把自己定义的类保存到散列表的时候,需要保证对象的 hashCode 不可变。

/**
 * 例1
 */
public class ChangeHashCode {
    public static void main(String[] args) {
        HashSet set = new HashSet();
        Person p1 = new Person(1001, "AA");
        Person p2 = new Person(1002, "BB");

        set.add(p1);
        set.add(p2);

        p1.name = "CC";//导致了内存的泄漏
        set.remove(p1); //删除失败

        System.out.println(set);

        set.add(new Person(1001, "CC"));
        System.out.println(set);

        set.add(new Person(1001, "AA"));
        System.out.println(set);

    }
}

class Person {
    int id;
    String name;

    public Person(int id, String name) {
        this.id = id;
        this.name = name;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Person)) return false;

        Person person = (Person) o;

        if (id != person.id) return false;
        return name != null ? name.equals(person.name) : person.name == null;
    }

    @Override
    public int hashCode() {
        int result = id;
        result = 31 * result + (name != null ? name.hashCode() : 0);
        return result;
    }

    @Override
    public String toString() {
        return "Person{" +
                "id=" + id +
                ", name='" + name + ''' +
                '}';
    }
}
(7)缓存泄露

内存泄漏的常见来源是缓存,一旦你把对象引用放入到缓存中,他就很容易遗忘。对于这个问题,可以使用 WeakHashMap 代表缓存,当发生GC的时候,如果WeakHashMap里面的key只有map本身引用时,就会将key对应的Entry清除掉。

public class MapTest {
    static Map wMap = new WeakHashMap();
    static Map map = new HashMap();

    public static void main(String[] args) {
        init();
        testWeakHashMap();
        testHashMap();
    }

    public static void init() {
        String ref1 = new String("obejct1");
        String ref2 = new String("obejct2");
        String ref3 = new String("obejct3");
        String ref4 = new String("obejct4");
        wMap.put(ref1, "cacheObject1");
        wMap.put(ref2, "cacheObject2");
        map.put(ref3, "cacheObject3");
        map.put(ref4, "cacheObject4");
        System.out.println("String引用ref1,ref2,ref3,ref4 消失");

    }

    public static void testWeakHashMap() {
        System.out.println("WeakHashMap GC之前");
        for (Object o : wMap.entrySet()) {
            System.out.println(o);
        }
        try {
            System.gc();
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("WeakHashMap GC之后");
        for (Object o : wMap.entrySet()) {
            System.out.println(o);
        }
    }

    public static void testHashMap() {
        System.out.println("HashMap GC之前");
        for (Object o : map.entrySet()) {
            System.out.println(o);
        }
        try {
            System.gc();
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("HashMap GC之后");
        for (Object o : map.entrySet()) {
            System.out.println(o);
        }
    }

}

上面代码和图示主演演示 WeakHashMap 如何自动释放缓存对象,当 init 函数执行完成后,局部变量字符串引用 weakd1,weakd2,d1,d2 都会消失,此时只有静态 map 中保存中对字符串对象的引用,可以看到,调用 gc 之后,HashMap 的没有被回收,而 WeakHashMap 里面的缓存被回收了。

三、拓展篇

3.1 Arthas

为什么有了之前提到过的工具还需要Arthas呢?

之前提到过的图形化工具有一个不可避免的缺点,都必须在服务端项目进程中配置相关的监控参数。然后工具通过远程连接到项目进程,获取相关的数据。这样就会带来一些不便,比如大部分线上环境的网络是隔离的,本地的监控工具根本连不上线上环境。

而Arthas,这款工具就不需要远程连接,也不需要配置监控参数,同时也提供了丰富的性能监控数据。

3.1.1 介绍

Arthas 是Alibaba开源的Java诊断工具,深受开发者喜爱。

当你遇到以下类似问题而束手无策时,Arthas可以帮助你解决:

  1. 这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception?

  2. 我改的代码为什么没有执行到?难道是我没 commit?分支搞错了?

  3. 遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗?

  4. 线上遇到某个用户的数据处理有问题,但线上同样无法 debug,线下无法重现!

  5. 是否有一个全局视角来查看系统的运行状况?

  6. 有什么办法可以监控到JVM的实时运行状态?

  7. 怎么快速定位应用的热点,生成火焰图?

  8. 怎样直接从JVM内查找某个类的实例?

Arthas支持JDK 6+,支持Linux/Mac/Windows,采用命令行交互模式,同时提供丰富的 Tab 自动补全功能,进一步方便进行问题的定位和诊断。

此处不过多介绍,详情请看:

Arthas教程

Arthas官网

如果有感兴趣的读者,可以在评论区回复想学习Arthas,后续可以出一篇大厂内部Arthas实战指南。

3.2 火焰图

为什么需要火焰图?

当我们的程序被越来越多的人访问的时候,我们此时就需要对程序的性能进行优化,来支撑更多的用户请求和达到更快的用户响应,这个时候我们用之前提到的JVM监控分析工具就不太行了,我们只能够知道相应的内存分布,线程的执行情况,但是无法从

什么是火焰图?

火焰图(Flame Graph)是由 Linux 能优化师 Brendan Gregg 发明的,和所有其他的 profiling 方法不同的是,火焰图以一个全局的视野来看待时间分布,它从底部往顶部,列出所有可能导致性能瓶颈的调用栈。它主要是用来进行分析性能优化

此处不过多介绍,详情请看:

如何读懂火焰图——阮一峰

巧用火焰图快速分析链路性能

3.3 GC日志分析 ⭐️

什么是GC日志分析?

GC日志指JVM在进行YGC和FullGC时的一些日志信息,通过分析GC日志,我们可以清楚在发生GC期间JVM的内存变化,从而针对具体的场景做一些GC相关的优化。

3.3.1 GC分类

针对 HotSpot VM 的实现,它里面的 GC 按照回收区域又分为两大种类型:一种是部分收集(Partial GC),一种是整堆收集(Full GC)

  • 部分收集(Partial GC):不是完整收集整个 Java 堆的垃圾收集。其中又分为:

    • 新生代收集(Minor GC / Young GC):只是新生代(Eden / S0, S1)的垃圾收集

    • 老年代收集(Major GC / Old GC):只是老年代的垃圾收集。目前,只有 CMS GC 会有单独收集老年代的行为。注意,很多时候 Major GC 会和 Full GC 混淆使用,需要具体分辨是老年代回收还是整堆回收。

  • 混合收集(Mixed GC):收集整个新生代以及部分老年代的垃圾收集。目前,只有 G1 GC 会有这种行为

  • 整堆收集(Full GC):收集整个 java 堆和方法区的垃圾收集。

3.3.2 GC日志相关JVM参数
  • -XX: +PrintGC ——表示打开简化的GC日志

  • -XX: +PrintGCDetails —— 打印内存回收详细的日志,并在进程退出时输出当前内存各区域分配情况

  • -XX: +PrintGCTimeStamps ——输出GC发生的时间戳,需要配合1、2使用

  • -XX: +PrintGCDateStamps ——输出GC发生的时间戳(以日期的形式),需配合1、2使用

  • -XX: +PrintHeapAtGC —— 输出每一次GC前后的堆信息,可以独立使用

  • -Xloggc:<file> —— 把GC日志写入到一个文件中去,而不是打印到标准输出中

下面就针对前两个重要的命令来分析一下GC日志的信息。

3.3.3 GC日志分类

由于PrintGCDetails打印的信息更加全面,所以一般通过该命令来进行GC日志分析。

(1)MinorGC(YGC)

下图表示了一个经典的MinorGC日志的信息和其信息代表的含义。

  • GC类型

  • 新生代

    • GC前后新生代内存占用

    • 新生代总大小(只包括1/2的Survivor区)

  • 堆空间

    • GC前后JVM堆内存占用

    • JVM堆总大小

  • GC耗时

    • user:CPU工作在用户态所花费的时间

    • sys:指的是CPU工作在内核态所花费的时间

    • real:指的是在此次GC事件中所花费的总时间

(2)FullGC

下图表示了一个经典的FullGC日志的信息和其信息代表的含义。

主要包括以下信息:

  • GC类型

  • 新生代

    • GC前后新生代大小

    • 新生代总大小

  • 老年代

    • GC前后老年代大小

    • 老年代总大小

  • 堆空间

    • GC前后堆内存占用

    • JVM堆总大小

  • 元空间

    • GC前后元空间大小

    • 元空间总大小

  • GC耗时

    • user:CPU工作在用户态所花费的时间

    • sys:指的是CPU工作在内核态所花费的时间

    • real:指的是在此次GC事件中所花费的总时间

可以看到,其实GC日志还是有规律的,一般都是按照先新生代,再老年代,再堆区域,再元空间的顺序依次展示,并且每个模块中的信息结构一致。

一般都是:[区域名称:GC 前内存占用-> GC 后内存占用(该区域内存总大小)]
3.3.4 GC日志分析工具

除了自己肉眼分析以外,还可以使用一些GC日志分析工具来分析,这里简单介绍一下。

(1)GCEasy

GCEasy 是一款在线的 GC 日志分析器,可以通过 GC 日志分析进行内存泄露检测、GC 暂停原因分析、JVM 配置建议优化等功能,大多数功能是免费的。

官网地址:gceasy.io/

(2)GCViewer

GCViewer 是一款离线的 GC 日志分析器,用于可视化 Java VM 选项 -verbose:gc 和 .NET 生成的数据 -Xloggc:。还可以计算与垃圾回收相关的性能指标(吞吐量、累积的暂停、最长的暂停等)。当通过更改世代大小或设置初始堆大小来调整特定应用程序的垃圾回收时,此功能非常有用。

源码下载:github.com/chewiebug/G…

运行版本下载:github.com/chewiebug/G…

(3)GChisto
  • 官网上没有下载的地方,需要自己从 SVN 上拉下来编译

  • 不过这个工具似乎没怎么维护了,存在不少 bug

(4)HPjmeter
  • 工具很强大,但是只能打开由以下参数生成的 GC log,-verbose:gc -Xloggc:gc.log。添加其他参数生成的 gc.log 无法打开

  • HPjmeter 集成了以前的 HPjtune 功能,可以分析在 HP 机器上产生的垃圾回收日志文件

【原理】OpenClaw Agent loop回顾一文清 OpenClaw智能体运行流程涉及从消息接收、上下文组装、模型推理到工具执行、流式回复及持久化的完整闭环。其核心机制包括会话队列管理(支持steer/followup等模式)、并发控制、提示词动态组装及工具用循环。系统通过文件锁、事件桥接压缩策略保障可靠性,同时提供灵活的队列配置与生命周期管理,实现高效、安全的智能体交互。流程严格遵循验证→推理→执行→回复的线性步骤,并支持多轮工具用迭代,最终将完整对话记录持久化存储。 阅读详情

相关推荐

十分钟学会排查线上JVM溢出

设置最大最小内存均为200mb为了快速OOM。

七叔的博客 29

搭建互联网架构学习--001--前言

前言: 自己也参与过app上的前后端分离项目,说是项目,其实就是app上一个新的功能。其他功能不相干,所以不是在公司app项目基础上做的新增,而是一个单独的maven项目,具体到代码,自己参与的还只是CRUD,写service,以至于再去面试,别人问到这个前后端分离的一些具体问题,自己就答不上来。离开了上家公司,所以想把前后端分离模式下的一些细节问题弄懂。在网上买的这个教程。题目是《...

weixin_30439067的博客 96

Cache一致性协议详解——从MSI到Directory-Based

摘要 《深入浅出多核 Cache 一致性协议》系统梳理了从 MSI 到 Directory-Based 的缓存一致性协议演化历程。文章从计算机存储体系结构出发,阐释了多核环境下缓存一致性问题产生的根源:为解决"存储墙"问题引入的多级缓存导致同一数据存在多个副本。随后详细分析了两种主流解决方案——基于总线监听的 Snooping 协议(包括 MSI、MESI、MESIF、MOESI 等)基于目录的 Directory-Based 协议,对比了它们在扩展性、带宽消耗等方面的劣。文章特别对各类协议的状态转换机

元直的博客 35

大学cs学习路线(主要java后端,包含前端、产品经理、Agent)

没必要太早学,我做项目的时候也没用到。

2403_82497315的博客 1226

RocketMQ 同一 JVM 启动多个 Producer 导致 NameServer 只连一个的问题

这个问题本质上不是 NameServer 配置没生效,而是:同一 JVM 下,多个 Producer 因为 ClientId 相同,底层共用了同一个。短期解法如果确实要一个 JVM 连多个 MQ 集群,就为不同 Producer/Consumer 设置不同的。长期解法如果只是集群迁移,直接统一所有到新地址,避免维护多套客户端资源。排查这类问题时,别只看业务代码里的,还要看到底层客户端实例是否被复用了。

阿昌爱Java 319

Linux-JVM线程查询

【代码】Linux-JVM线程查询。

u013578042的博客 50

LangGraph 会话切换与短期记忆管理

本文介绍了使用LangGraph构建Agent应用时的会话记忆管理方案。核心是通过Checkpointer机制实现多轮对话跟踪会话隔离,重点演示了基于InMemorySaver的内存存储实现方式。文章包含以下要点: 基础会话管理:利用thread_id实现多会话隔离,相同ID复用历史,不同ID创建独立会话 交互式终端实现:通过命令行指令支持会话切换、历史查看消息清空 记忆系统架构:Checkpointer自动管理会话轨迹,Store用于业务数据存储 开发试方案:InMemorySaver适合本地测试,

微软技术分享 234

热门八股-JVM

本文系统梳理了JVM核心知识点,主要内容包括: 内存结构:详细解析JVM运行时数据区(程序计数器、虚拟机栈、堆等)及直接内存的特性与应用场景。 类加载机制:深入讲解双亲委派模型及其缺点,分析打破委派的场景,并给出自定义类加载器实现方法。 垃圾回收:对比引用计数与可达性分析,解析分代收集原理(新生代复制算法/老年代标记整理),列举常见GC器特点,说明三色标记法原理。 实战:总结OOM类型与排查流程,提供FullGC、内存泄漏、CPU飙高等问题的诊断方法,介绍jstack/jmap等工具使用技巧。

2601_95495418的博客 164

CPU 100% 排查实战复盘:4步从确认到根治(附命令)

线上 CPU 100%、服务卡死,重启三次还是烧。很多人本能反应又是硬件不够了、加机器,结果过会儿又满。本文用 4 步法,从确认 CPU 打满到根治,每一步都讲清"为什么这么查",附可直接抄的命令。防汛防台巡检期,机房偏偏怕这种单核被打满的慢病。

ITyunwei0987的博客 260

Java面试题——JVM(二)

Java堆内存中,分代垃圾回收是一种常用的垃圾回收方式。其中,新生代主要负责存储短期存活的对象,老年代则主要存储长期存活的对象。新生代GC、老年代GC完全GC是三种不同的GC方式,它们的特点如表所示。 JDK预设的垃圾回收器的特点如表所示。 垃圾回收器的性能评估指标如表所示。 在JDK 17中,G1垃圾回收器是默认的垃圾回收器。G1垃圾回收器在JDK 7中首次被引入,并在随后的JDK版本中进行了改进化。G1垃圾回收器是一个高效的垃圾回收器,其特点是可以高效地管理大内存、具有可预测的停顿时间高吞吐

吴声子夜歌的博客 203

G1 线程本地分配缓冲:TLAB、PLAB 与同源设计

本文把 G1 里几处"给每个线程/worker 发一块私有缓冲、平时各写各的不碰锁、攒够再刷回共享结构"的设计放在一起进行解读。

月华的博客 172

零基础入门Python14|SQLite与SQL CRUD:建立图书数据库

图中最重要的边界是事务:只有成功路径才提交数据,失败路径不能留下半条业务记录。

a1250467048的博客 225

【Android 内存管理】JVM 内存模型与 GC 原理

Android内存

studyForMokey的博客 349

Java面试题——JVM(一)

Java中,运行时常量池是方法区(在Java 8之前的实现方式为永久代,Java 8及之后的实现方式为元空间)的一部分,用于存储编译期已确定的常量。其中,​“GC Roots”是算法的起始点,通常包括以下几种对象:虚拟机栈(栈帧中的本地变量表)中的引用对象、类静态属性中的引用对象、类常量中的引用对象及JNI中的引用对象。此外,Java编译器在编译时还会进行一种称为“逃逸分析”的化分析,它可以动态地确定对象的作用域生命周期,从而避免不必要的对象分配垃圾回收操作,进一步提高应用程序的性能

吴声子夜歌的博客 180

Go-GC垃圾回收机制深度从三色标记到内存

GOGC控制GC触发频率设置内存软限制减少堆分配是降低GC压力的根本三色标记保证并发安全。

qq_31142761的博客 74
上一篇: Springboot+Shiro+Jwt实现简单的权限控制
下一篇: Synchronized原理分析
悲伤荷包蛋hb
博客等级 码龄5年 3169粉丝 319原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值