类加载 双亲委派 内存模型 对象内存分配 逃逸分析学习记录

类加载+双亲委派

main方法运行过程

C++语言实现的java.exe来创建jvm,和引导类加载器,并由引导类加载器来创建类加载器的启动器launcher,在类加载器启动器空参构造中就对剩下的拓展类加载器,应用程序加载器,和自定义加载器来进行了加载(本质上也是一个URL类加载器,通过类的路径来加载),加载完成之后,就会调用main方法,来启动程序;

类加载过程

加载:

将磁盘上的class文件,通过类加载器,加载到内存中,并生成一个class对象,包含了class文件中的信息;属性方法等;

类被加载到方法区后,主要包括,运行时常量池,类型信息,字段信息,方法信息,类加载器的引用,对应class实例的引用等信息;

类加载器的引用:这个类加载器实例的引用

对应class实例的引用:类加载器在加载类信息放到方法区中后,会创建一个对应的class类型的对象实例放到堆(heap)中,作为开发人员访问方法区中类定义的入口和切入点;

Jdk进行类加载的机制可以看成懒加载,只有在真正用到的时候才回去加载;

验证:

验证文件的格式的正确性,class字节码文件是16进制文件;

字节码文件(要符合一定规则)

比如开头的cafe babe标志,以及上面的0034转换成16进制就是52,代表JDK版本为JDK8;

准备:

对于加载阶段创建的class对象,因为静态变量属于class对象,这时就可以对静态变量分配内存,赋予默认值;

解析:

静态链接的过程;

符号引用:java文件中的一些关键字,变量类型,变量名,符号等,在解析的时候都会放到常量池,成为静态变量

直接引用:真实的内存地址

对于一些静态方法,或者java定义好的修饰符,关键字,符号等替换为直接引用,因为这些是JDK就规定好了的,一旦被加载到内存,它的地址不会改变,这就是静态链接;

对于

Java -p java提供的查看字节码文件工具,更便于我们浏览

Constant pool常量池,#1这种相当于它的标识,相当于序号,也可以说是key

Methodref:方法引用

Invokespecial在进行方法调用

动态链接:Math.compute();中compute在加载的时候不会去解析,真正运行的时候才会区解析,才会把符号链接到在内存中的真正地址;

可以理解为属于JDK,或者class对象的符号,用的是静态链接,属于正常的bean对象的用的是动态链接;

初始化

对静态变量 进行初始化,并执行静态代码块;

类加载过程中,执行静态代码块,在进行构造初始化init;

类加载器和双亲委派机制

类的加载过程主要有类加载器来完成,类加载器有

  1. 引导类加载器bootstrapclassloader,加载负责支撑JVM运行的核心类库,经常用到的JDK自带的类,String,Math,debug查看它为null,因为它是c实现的,不是一个java对象;
  2. 拓展类加载器extclassloader,加载支撑JVM运行的拓展jar,lib下的ext包
  3. 应用程序加载器appclassloader,加载程序中classpath,也就是JVM自带的之外的自定义的类
  4. 用户自定义类加载器,加载指定的类

类加载器中,都会保存一份它的父的类加载器,

查看引导类加载器加载的路径;

这个是appclassloader,加载的包,它不仅加载了我们定义的,它还包含了JDK的核心类库包,以及拓展类包,上面说核心类库由引导类加载器加载,拓展类包由拓展类加载器extclassloader加载,而应用程序加载器appclassloader的加载包中却有这些,这里就引出了双亲委派机制

双亲委派加载过程图解

  1. 向上委托查找,查看当前加载器是否加载过该类,没有则交给父加载器加载;
  2. 委托查找时,每个加载器加载过的类,都会有一个缓存,向上查找就是查找加载的class缓存;
  3. 向下加载,当顶层加载器引导类加载器,都没有加载过,那就让引导类加载器进行加载,如果有加载权限,就加载,没有加载权限,则交给子加载器加载;

向上查找就是为了保护JDK自己的类,不被用户自定义的加载器加载;而且为了保护不被其它自定义的加载器加载,还有个沙箱保护机制,当我们自定义类的包名,不允许和JDK定义好的包名一致,在类加载的过程中会进行判断;

全盘负责委托机制

当一个classloader装载一个类时,除非显示的使用另外一个classloader,装载该类时,需要引用的其它类也由该classloader加载;

打破双亲委派机制

双亲委派的代码,在classloader中实现的,向上查找,向下加载,findclass()

protected Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException
{
    synchronized (getClassLoadingLock(name)) {
        // First, check if the class has already been loaded
        Class<?> c = findLoadedClass(name);
        if (c == null) {
            long t0 = System.nanoTime();
            try {//这里 先去判断父类是否为空,为空说明是引导类加载器,向上查找的时候,查到引导类加载器的时候,


                if (parent != null) {
                    c = parent.loadClass(name, false);
                } else {
//这里去引导类加载器中去查找
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
                // ClassNotFoundException thrown if class not found
                // from the non-null parent class loader
            }

            if (c == null) {
//如果引导类加载器也没有加载过,
                // If still not found, then invoke findClass in order
                // to find the class.
                long t1 = System.nanoTime();
//这里正真的去findclass加载
                c = findClass(name);

                // this is the defining class loader; record the stats
                sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                sun.misc.PerfCounter.getFindClasses().increment();
            }
        }
        if (resolve) {
            resolveClass(c);
        }
        return c;
    }
}
 

以上源码可以看出,它先查找了当前类加载器有没有加载,没加载再去父类loadeclass()加载,

所以这里我们可以继承一个loadclass,重写loadclass方法,小小的改动,只要让它查找不到的时候不去用父类去loadeclass,而是直接findclass();修改后如下;

protected Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException
{
    synchronized (getClassLoadingLock(name)) {
        // First, check if the class has already been loaded
        Class<?> c = findLoadedClass(name);
        if (c == null) {
//这里直接当前加载器加载
                    findclass(name);
        if (resolve) {
            resolveClass(c);
        }
        return c;
    }
}
 

这里直接这么改的话,会出现问题,因为之前提到的全盘负责委托机制,我们定义的类中,它所有的类都由我们自定义的加载器加载,而有的类只能是引导类加载器加载,所以我们只需要在原来的基础上做小改动

protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
{
    synchronized (getClassLoadingLock(name)) {
        // First, check if the class has already been loaded
        Class<?> c = findLoadedClass(name);
        if (c == null) {
            long t0 = System.nanoTime();
            try {
                //这里添加条件 当然我这里只是粗略的判断了包名
                if(name.contains("com.chenlei")){
                    c = findClass(name);
                }else {
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        c = findBootstrapClassOrNull(name);
                    }
                }
               
            } catch (ClassNotFoundException e) {
                // ClassNotFoundException thrown if class not found
                // from the non-null parent class loader
            }

            if (c == null) {
                // If still not found, then invoke findClass in order
                // to find the class.
                long t1 = System.nanoTime();
                c = findClass(name);

                // this is the defining class loader; record the stats
                sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                sun.misc.PerfCounter.getFindClasses().increment();
            }
        }
        if (resolve) {
            resolveClass(c);
        }
        return c;
    }
}

Tomcat的一个WEB容器,可能是需要部署多个应用程序,多个war包,不同的应用程序,可能依赖的同一个第三方类库的不同版本,因此要保证每一个应用有它自己的独立的类库,来保证相互隔离;

Tomcat的应用隔离,它每个应用有自己的webappclassloader,用来加载自己目录下的class文件,不会传递给父类加载器,打破了双亲委派机制;

热部署原理,当加载了类过后,卸载加载器,然后再次需要的话,重新加载;

同一个JVM中,不同路径下,包名相同,类名相同,不同的classloader;这就相当于tomcat加载

JVM内存模型

JVM虚拟机的组成

类装载子系统

执行引擎

运行时数据区

  1. 类装载子系统

class文件,通过类装载子系统,把class文件放到了数据区,然后通过执行引擎来执行;

堆(Heap)

GC:执行引擎在后台会维护一个GC线程来做对象回收;

堆分为年轻代和老年代,年轻代又分为eden(伊甸园区)和servivor区,它们的默认内存大小比例为老年代:年轻代=2:1;eden:s0:s1=8:1:1;

新建的对象,都会放入Eden中,当Eden中的内存地址被对象放满之后,就会触发young_GC,从GC_ROOT根节点根据引用链来判断是否是需要回收的对象;如果是存在引用,那么这个对象以及引用链上的对象都会认为是有效对象,就会从Eden区移动到servivor区,并且分代年龄加1;剩余的留在Eden区的对象就会被认为是垃圾对象,进行回收;

当Eden重复被放满的情况下,有效对象会在s0和s1之间来回挪动,每次挪动分代年龄+1,当分代年龄达到一定值后,就会向把对象放在老年代中,或者来回挪动时,s0或者s1放不下了,也会放到老年代中;

当老年代被放满时,就会触发一次full_GC,对整个堆和方法区来进行一次回收,当回收过后,老年代还是放满的,这时再有对象放入老年代时,就会OOM;

Java 提供的JVM监控控工具 jvisualvm

STW机制

Stop the world,当jvm进行GC的时候,会把工作线程全部暂停,用户就会感觉是程序卡了一样,特别是FULL_GC它扫描的需要进行GC的区域比较大,STW的时间就会更长;

设置STW机制的原因,个人理解,有点像数据库各级级别中的可重复读,可重复读它可以粗略的理解成是一个快照,每次读的数据一样;这里就是要确保扫描结果的准确性,和一致性(GC完之后和此时内存中对象状态一致),但这里它是直接不再产生新对象,避免扫描的结果不确定;比如一边生成对象,或者方法出栈,局部变量对象的引用发生改变,一边之前扫描过的对象前一秒还是有效的,后一瞬间就变成了垃圾对象,之前的扫描感觉就像是无效的;

栈(stack)

不同的线程,在执行代码的时候,都会有自己的栈(所以也可以称为线程栈),来存放当前线程的局部变量,当方法被执行时,也会马上在在线程栈中分配一个栈帧,把不同方法的局部变量隔离开来;

方法进栈的原则是先进后出,后进先出;因为后进栈的方法先执行完,执行完它先出栈的话,它局部变量也就用完了,就能先释放掉内存空间;

栈帧:

方法进栈时分配的内存,主要含有局部变量表,操作数栈,动态链接,方法出口

局部变量表:

存放局部变量,其实是局部变量在内存中的地址,真正的局部变量对象放在堆中;

操作数栈:

需要进行修改计算等操作的数据的值,都是加载到操作数栈中,然后CPU从操作数栈中去取要进行操作的变量,然后把操作计算完的结果再压回操作数栈中;相当于一个临时中转站;

动态链接:

在运行的时候,存放调用的非静态的方法在内存中的实际地址;

方法出口:

记录当前方法调用完毕,下面该执行哪一行;

程序计数器

用来记录当前线程代码执行的位置;因为代码是由执行引擎来执行的,它执行完一行代码,字节码执行引擎都会去修改它到对应的位置;

这主要是考虑到多线程的情况,便于进行线程恢复的时候定位;

注:紫色的部分是每个线程都会有的,不会共享;

黄色是所有线程共享的;

方法区(元空间)

运行时常量(比如final修饰的)+静态变量+类信息,比如 static User user= new User();这时user对象就是一个静态变量,它就会在方法区中,存放一份user对象在堆中的地址;

本地方法栈

本地方法是指,JDK提供的,由java去调用其它语言的接口,当本地方法调用的时候,不管什么语言实现的,它都会在内存中分配内存空间,这个内存空间就是分配在本地方法栈;

也就是线程在调用本地方法的时候分配的内存空间;

总结

jvm虚拟机中,堆和方法区是所有线程共享的部分;

栈和程序计数器是线程创建的时候,分配的,如果要进行一些本地方法的调用,线程还会分配一个本地方法栈,这些在线程之间都不是共享的;

方法区中静态变量,如果这个静态变量刚好是我们创建的对象,也是只存放了堆中创建对象的地址;

JVM内存参数设置

-XX:MetaspaceSize

-XX:MaxMetaspaceSize

对于方法区,它存放了类信息,它直接使用的是物理内存,而且有一个内存空间的自动伸缩机制,我们不去设置值的时候,它的默认值是21M,当21M内存被放满后,就会触发FULL_GC,会根据FULL_GC之后,释放的空间大小,判断对它的值进行扩大还是缩小;所以合理设置元空间的初始内存和最大内存,能有效减少FULL_GC次数;

-Xss

它也是直接使用的是物理内存,是用来设置每个线程分配的栈空间的大小,默认为1M,当方法不停的嵌套,然后进栈不出栈的情况下,栈总会被放满的,合理设置可以减少程序出错,导致递归调用的次数,也能满足日常调用而尽可能少的出现栈溢出情况;

而且栈分配的小,同等情况下,理论上能开启的线程数也越多;

对象创建过程

内存分配:

划分内存的方法,

指针碰撞:在JVM的堆中,对象是有序存放的,空闲内存和占用内存是有一个指针作为分界点,当新建一个对象的时候,就把分界点挪动和新建对象大小一样的距离;

空闲列表:JVM堆中的对象并不是有序繁殖的,所以JVM维护一个列表,来标识哪些内存空间还是空闲状态,分配的时候从空闲里面划分一个能放置新建对象的内存,然后更新列表;

不管是哪种分配方式,当多个线程去创建对象,同时需要在堆中进行内存分配,就会存在并发的问题;

内存分配时并发问题的解决

CAS

几个线程同时抢占这一块空间,谁抢到谁使用,没抢到的加上重试机制;、

本地线程分配缓冲

-XX:+/-UsrTLAB 设置虚拟机是否开启本地线程分配缓冲

-XX:TLABSize 指定TLAB的大小;默认是伊甸园区的1%;

提前给线程在堆中分配好一块专属的内存空间;JDK8默认开启;

初始化

属于对象的成员变量,也会先给一个默认的初始值,

设置对象头

标记字段(Mark Word):

类型指针(Klass pointer):堆内存中对象头里边,存放的地址,指向方法区中的类信息;

类信息:粗略理解为存放了class代码信息,用c和c++实现的对象;而class对象是提供给java开发人员的一个java对象,放在堆中;

对象在内存中的分布,其中object header是对象头,除了后边有一个alignment是对其补充,其它的是实例数据;aliment/padding是实例数据内部的对其;为了满足对象大小为8bit的倍数,这样读取效率更高;

对象的指针压缩

将对象头中的类型指针,和数据进行指针压缩,减小对象占用内存的大小,JDK1.6之后默认开启的;

内存和操作系统位数的关系

内存是需要指针来进行表示的,32位指针,用二进制来表示就是能存2的32次方个数,也就是能代表2的32次方个内存地址,转换成内存大小就是4个G;

正常32位的指针,只能表示4个G的内存,如果是16G的内存,本来是用2的35次方也就是至少35位的操作系统,来表示,但是有了指针压缩过后,就能用32位地址,就能支持16G内存;

当堆内存大于32G的时候,指针压缩会失效,会强制使用64位,来堆java空对象寻址,

Init方法

给对象的成员变量真正赋值,并调用构造方法;

对象内存分配

TLAB指开启本地线程分配缓冲,在伊甸园区提前分配堆内存,来提高并发;

-XX:+DoEscapeAnalysis逃逸分析

JVM可以通过设置-XX:+DoEscapeAnalysis,来确定是否开启逃逸分析,在进行内存分配的时候,会先去进行对象逃逸分析;JDK7之后默认开启;

对象逃逸分析:也就是分析对象的作用域,当一个对象在方法中被定义,如果它可能会在别的地方引用了,那么就可以人为它的作用域逃出了当前方法;比如test1,当它不会再别的地方被引用,作用域很确定只在当前方法中,那么就认为它没有逃逸;

对于没有逃逸的对象,就可以在栈上的方法的栈帧内存里分配内存空间,当方法出栈的时候,对象占用的内存空间随着出栈就销毁了,减轻了GC的压力;

所以不是所有的创建的对象都在堆内存中;

XX:+EliminateAllocations标量替换:

通过逃逸分析,不会逃逸的对象,并且可以被进一步分解时,JVM不会创建该对象,可以在栈帧中分配内存空间,但是栈帧的内存空间本来就不够大,可能没有一块连续的空间来存储当前对象,它就把对象拆分,然后放置到栈帧中的不连续的内存空间中;

逃逸分析和标量替换同时打开的时候,对于会逃逸的对象,在栈上分配内存,对于不逃逸的对象,进行标量替换来分解对象,也放在栈上这样可能会大量减少GC次数;

对象在年轻代中的分配 -XXUserAdaptiveSizePolicy年轻代内存分配自动变化

Young GC:新生代发生的垃圾搜集动作,回收频繁,回收速度快;

Full GC:回收堆中老年代,年轻代和方法区中的垃圾;一般回收速度会慢10倍以上;

因为Eden区的对象大多都是朝生夕死,存活时间很短,所以年轻代中让Eden区尽量大是合理的,默认8:1:1,JVM默认开启-XXUserAdaptiveSizePolicy,会导致比例自动变化,不想自动变化可以关闭;

大对象直接进入老年代

大对象是指需要大量连续空间的对象,JVM参数 -XX:PretenureSizeThreshold可以定义大对象的大小,超过这个值,直接放到老年代中,这个参数只在Serial(-XX:UseSerialGC)和ParNew两个收集器中有效;

避免在年轻代中来回复制,大对象复制慢,降低性能;

长期存活的对象尽早放进老年代

可以通过设置对象年龄参数,来控制长期存活数据放入老年代的时间,如果大部分对象都是一次性的,或者是长期存活的对象,就可以适当调小年龄,来让对象尽早进入老年代;通过参数-XX:MaxTenuringThreshold来设置;

对象动态年龄判断机制

当一批对象从Eden区,或者从一个servivor区,放入另一个servivor区时,如果这一批对象的总大小,大于servivor区的50%,那么准备放入对象的这个servivor区中,年龄大于这一批对象中最大的年龄的数据,都会放入老年代;这就是动态年龄判断机制,它 一般实在有young GC之后触发

对象内存回收

如何确定对象是否位垃圾对象;

  1. 引用计数算法

当对象,在内存中,每有一个引用就对它进行引用计数+1;当引用失效就-1,当引用计数为0时,对象被视为垃圾对象;JVM一般不用这种算法,它比较难解决对象之间的相互引用的问题;

  1. 可达性分析算法

将GC-ROOT对象作为起点,从这个的节点向下搜索引用对象,找到的对象标为非垃圾对象,

GC-ROOT根节点:线程栈的本地变量,静态变量,本地方法栈的变量等;

常见的引用类型

方法区中回收主要回收无用的类

除了自定义加载器,JDK提供的Classloader 几乎不会被回收掉,所以如果是通过应用类加载器,拓展类加载器,和引导类加载器来加载的class,那么它只要加载过一次,方法区中的类就不会被回收;Tomcat热部署是通过卸载加载器,来实现的,类加载器是重新创建的,所以这种自定义加载器加载的类是可以进行回收的;

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

前端第一天练习

一、安装Node.js和搭建前端环境 安装 Node.js&#xff1a;访问 Node.js 官网 下载并安装最新版本的 Node.js。 LTS 为长期稳定的版本, Current 为新特性尝鲜版本, 可能存在隐藏的 bug 或者安全漏洞打开终端, 在终端输入命令 node -v后, 即可查看已安装的 Node.js 版本号 使用 …

JWFD流程图转换为矩阵数据库的过程说明

在最开始设计流程图的时候&#xff0c;请务必先把开始节点和结束节点画到流程图上面&#xff0c;就是设计器面板的最开始两个按钮&#xff0c;先画开始点和结束点&#xff0c;再画中间的流程&#xff0c;然后保存&#xff0c;这样提交到矩阵数据库就不会出任何问题&#xff0c;…

linux命令之tput

1.tput介绍 linux命令tput是可以在终端中进行文本和颜色的控制和格式化&#xff0c;其是一个非常有用的命令 2.tput用法 命令&#xff1a; man tput 3.样例 3.1.清除屏幕 命令&#xff1a; tput clear [rootelasticsearch ~]# tput clear [rootelasticsearch ~]# 3.2.…

32-2 APP渗透 - 移动APP架构

前言 app渗透和web渗透最大的区别就是抓包不一样 一、客户端: 反编译: 静态分析的基础手段,将可执行文件转换回高级编程语言源代码的过程。可用于了解应用的内部实现细节,进行漏洞挖掘和算法分析等。调试: 排查软件错误的一种手段,用于分析应用内部原理和行为。篡改/重打…

数据分析之POWER Piovt透视表分析

将几个数据表之间进行关联 生成数据透视表 超级透视表这里的字段包含子字段 这三个月份在前面的解决办法 1.选中这三个月份&#xff0c;鼠标可移动的时候移动到后面 2.在原数据进行修改 添加列获取月份&#xff0c;借助month的函数双击日期 选择月份这列----按列排序-----选择月…

wps斜线表头并分别打字教程

wps斜线表头怎么做并分别打字&#xff1a; 1、首先选中我们想要设置的表头。 2、接着右键选中它&#xff0c;点击“设置单元格格式” 3、然后点击上方“边框”选项卡。 4、随后选择图示的斜线&#xff0c;点击“确定” 5、设置完成后&#xff0c;我们只要在其中打字就可以在斜…

UniFace:深度人脸识别的统一交叉熵损失

UniFace: Unified Cross-Entropy Loss for Deep Face Recognition softmax损失 缺点&#xff1a;不能保证最小正样本类相似度大于最大负样本类相似度 问题&#xff1a;没有统一的阈值可用于将正样本与类对与负样本与类对分开 创新点 设计了用于人脸识别模型训练的UCE&#xf…

设计模式之装饰模式精讲

概念&#xff1a;动态地给一个对象添加一些额外的职责。 装饰器模式侧重于在不改变接口的前提下动态地给对象添加新功能&#xff0c;保持对象结构的透明性&#xff0c;客户端无感知。 以一个咖啡制作和装饰的例子来帮助大家理解&#xff1a; public interface Coffee {double…

汽车租赁(源码+文档)

汽车租赁&#xff08;小程序、ios、安卓都可部署&#xff09; 文件包含内容程序简要说明含有功能项目截图客户端登录界面首页订单个人信息我的界面新手指引注册界面车型选择支付界面修改信息 管理端用户管理订单管理分类管理 文件包含内容 1、搭建视频 2、流程图 3、开题报告 …

校园招聘管理系统(源码+文档)

校园招聘管理系统&#xff08;小程序、ios、安卓都可部署&#xff09; 文件包含内容程序简要说明含有功能项目截图客户端热门岗位校园招聘首页个人简历添加个人简历我的界面注册界面查看个人简历界面个人资料界面登录界面消息界面退出登录 管理端登录界面![请添加图片描述](htt…

【shell】select in实现终端交互场景

文章目录 序言1. select in语句及其语法2. select in和case语句相结合3. 执行界面示例 序言 shell脚本实现简单的终端交互功能&#xff0c;根据用户不同输入执行不同功能脚本 1. select in语句及其语法 select in是shell独有的一种循环&#xff0c;非常适合终端交互场景 该语…

什么是nginx正向代理和反向代理?

什么是代理&#xff1f; 代理(Proxy), 简单理解就是自己做不了的事情或实现不了的功能&#xff0c;委托别人去做。 什么是正向代理&#xff1f; 在nginx中&#xff0c;正向代理指委托者是客户端&#xff0c;即被代理的对象是客户端 在这幅图中&#xff0c;由于左边内网中…

数据结构(六)——图的存储及基本操作

6.2 图的存储及基本操作 6.2.1 邻接矩阵法 邻接矩阵存储无向图、有向图 #define MaxVertexNum 100 //顶点数目的最大值typedef struct{char Vex[MaxVertexNum]; //顶点表int Edge[MaxVertexNum][MaxVertexNum]; //邻接矩阵&#xff0c;边表int vexnum,arcnum; //图的当前…

pytest--python的一种测试框架--pycharm创建项目并进行接口请求

前言 学习request的使用&#xff0c;在用之前&#xff0c;用官方文档提供的接口&#xff1a;https://api.github.com/events&#xff1b; ctrl鼠标左键可以进入被调用函数源码&#xff0c;可以看到第一个参数URL是必须参数&#xff0c;params是选填&#xff0c;**kwargs是关键…

【论文通读】AutoGen: Enabling Next-Gen LLM Applications via Multi-Agent Conversation

AutoGen: Enabling Next-Gen LLM Applications via Multi-Agent Conversation 前言AbstractMotivationFrameworkConversable AgentsConversation Programming ApplicationA1: Math Problem SolvingA2: Retrieval-Augmented Code Generation and Question AnsweringA3: Decision…

HarmonyOS 应用开发之FA模型启动Stage模型UIAbility

本文介绍FA模型的三种应用组件如何启动Stage模型的UIAbility组件。 PageAbility启动UIAbility 在PageAbility中启动UIAbility和在PageAbility中启动PageAbility的方式完全相同。 import featureAbility from ohos.ability.featureAbility; import { BusinessError } from oh…

Roxlabs代理服务:智能化数据采集的加速器

TOC 一、引言 在这个数据驱动的时代&#xff0c;无论是企业还是个人&#xff0c;对于准确、及时的信息获取都有着前所未有的需求。网络数据采集已成为洞察市场趋势、分析竞争对手动态、优化营销策略的关键手段。然而&#xff0c;面对全球范围内的网站和服务&#xff0c;如何高…

数据结构与算法 循环双链表基本运算与对称算法

一、实验内容 1、实现循环双链表的各种基本运算的算法 &#xff08;1&#xff09;初始化循环双链表h &#xff08;2&#xff09;依次采用尾插法插入a,b,c,d,e元素 &#xff08;3&#xff09;输出循环双链表h&#xff1b; &#xff08;4&#xff09;输出循环双链表h长度&am…

Linux初学(十一)中间件

一、web服务 1.1 中间件简介 中间件其实就是一类软件&#xff0c;中间件的作用是让用户可以看到一个网页 总结&#xff1a;客户端可以向服务端发送请求&#xff0c;服务器端会通过中间件程序来接收请求&#xff0c;然后处理请求&#xff0c;最后将处理结果返回给客户端 1.2 中…

vscode初始化node项目

首先需要安装node环境&#xff0c;推荐直接使用nvm 安装node&#xff0c;方便切换node版本 1.npm init 初始化node项目 在命令行输入npm init指令 根据指令创建完成后会在当前目录下生成一个package.json文件&#xff0c;记住运行npm init执行的目录必须是一个空目录 2.创建…