Qt内存泄漏与程序异常崩溃

news2024/11/25 6:53:23

内存泄漏

什么是内存泄漏?

内存泄漏(Memory Leak)指的是程序在动态分配内存后未能正确释放已分配的内存,导致这些内存块无法被再次使用或回收。内存泄漏的发生主要是在使用堆内存(通过newmalloc分配的内存)时没有调用相应的deletefree来释放内存。

由于Qt是一个c++的框架,谈论Qt内存泄漏本质上就是在说c++内存泄漏

c++内存分配

谈内存泄露之前,需要先知道c++的内存分配情况

(1)栈区由编译器自动分配释放,存放为函数运行的局部变量,函数参数,返回数据,返回地址等。操作方式与数据结构中的类似,栈区有以下特点:

  1)由系统自动分配。比如在函数运行中声明一个局部变量int b = 10;,系统自动在栈中为b开辟空间;

  2)只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。

(2)堆区:一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收;分配方式类似于链表,堆区有以下特点:

  1)需要程序员自己申请,并指明大小,在C中是有malloc函数,在C++中多使用new运算符(从C++角度上说,使用new分配堆空间可以调用类的构造函数,而malloc()函数仅仅是一个函数调用,它不会调用构造函数,它所接受的参数是一个unsigned long类型。同样,delete在释放堆空间之前会调用析构函数,而free函数则不会)。

  2)在操作系统中有一个记录空闲内存地址的表,这是一种链式结构。它记录了有哪些还未使用的内存空间。当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序。

(3)全局数据区:也叫做静态区,存放全局变量,静态数据。程序结束后由系统释放

(4)文字常量区:可以理解为常量区,常量字符串存放这里。程序结束后由系统释放。“常量”是指它的值是不可变的,同时,虽然常量也是存储在内存的某个地方,但是无法访问常量的地址的。

(5)程序代码区:存放函数体的二进制代码。但是代码段中也分为代码段和数据段。

关于C++delete于new

在C++中,我们经常使用new操作符来进行内存分配,其内部主要做了两件事: 通过operator new从堆上申请内存(glibc下,operator new底层调用的是malloc) 调用构造函数(如果操作对象是一个class的话) 对应的,使用delete操作符来释放内存,其顺序正好与new相反: 调用对象的析构函数(如果操作对象是一个class的话) 通过operator delete释放内存

Qt内存管理

Qt 的内存管理机制相较于纯 C++ 更为高级和自动化。主要通过父子关系和智能指针来管理对象的生命周期,减少内存泄漏的风险。

1. 父子关系机制

Qt 提供了一种父子关系机制,通过设置父对象,自动管理子对象的生命周期。这种机制主要依靠 QObject 类及其派生类来实现。如果其 parent 非 nullptr,那么其 parent析构时会自动析构该对象。Qt 使用父子关系来管理对象的生命周期,确保子对象在父对象销毁时自动被销毁,从而避免内存泄漏。
- 通过父对象的 QObject构造函数传递父对象指针,或者使用setParent()方法来设置父对象。

#include <QApplication>
#include <QPushButton>

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);

    QWidget window;

    // 使用父子关系,按钮的内存由窗口管理
    QPushButton *button = new QPushButton("Click me", &window);

    window.resize(200, 100);
    window.show();

    return app.exec();
}

在上述代码中按钮 button的父对象是窗口 window。当 window 被销毁时button也会自动被销毁。

2.QWidget 及其派生类的对象

 可以设置 Qt::WA_DeleteOnClose 标志位。当窗口关闭时,该对象会被自动析构。这对于临时窗口或对话框非常有用。

   QWidget *window = new QWidget;
   window->setAttribute(Qt::WA_DeleteOnClose);
   window->show();

3. QAbstractAnimation 派生类的对象

可以设置 QAbstractAnimation::DeleteWhenStopped属性。当动画停止时,该对象会自动删除。

  QPropertyAnimation *animation = new QPropertyAnimation(object, "geometry");
   animation->setDuration(1000);
   animation->setStartValue(QRect(0, 0, 100, 30));
   animation->setEndValue(QRect(250, 250, 100, 30));
   animation->setEasingCurve(QEasingCurve::OutBounce);
   animation->setDeleteWhenStopped(true);
   animation->start();

4. QRunnable::setAutoDelete() 和 MediaSource::setAutoDelete()

QRunnable对象可以使用 QRunnable::setAutoDelete(true) 设置为自动删除。当 QRunnable 对象完成执行时,会自动删除。
类似地,MediaSource对象也可以通过 MediaSource::setAutoDelete(true)设置为自动删除

5. 智能指针

C++11 引入了智能指针,Qt 也提供了一些智能指针类,如 `QScopedPointer`、`QSharedPointer` 和 `QPointer`。

QScopedPointer

QScopedPointer 是一个简单的智能指针类,用于管理动态分配的对象。当 QScopedPointer 超出作用域时,自动删除其管理的对象。

#include <QScopedPointer>
#include <QPushButton>

void createButton() {
    QScopedPointer<QPushButton> button(new QPushButton("Click me"));
    // button 在函数结束时自动删除
}
QSharedPointer

QSharedPointer 是一个引用计数智能指针类,用于共享所有权。当最后一个 QSharedPointer被销毁时,删除其管理的对象。

#include <QSharedPointer>
#include <QPushButton>

void sharedButton() {
    QSharedPointer<QPushButton> button1(new QPushButton("Click me"));
    {
        QSharedPointer<QPushButton> button2 = button1;
        // button1 和 button2 共享同一个 QPushButton 对象
    }
    // button2 超出作用域,引用计数减少但对象不删除
    // button1 超出作用域,引用计数为零,对象删除
}
QPointer

QPointer是一个弱指针,用于监视 QObject 派生类对象。当被监视对象被销毁时,QPointer 自动变为 nullptr,避免空指针。

#include <QPointer>
#include <QPushButton>

void weakPointer() {
    QPointer<QPushButton> button(new QPushButton("Click me"));
    delete button;
    // button 现在为 nullptr,避免悬空指针
}

6. QObjects 和子对象的自动销毁

除了通过父子关系和智能指针管理内存,Qt 中所有继承自 QObject的对象也可以利用其内置的子对象管理功能。在对象的析构函数中,会自动销毁所有子对象。

class MyWidget : public QWidget {
public:
    MyWidget(QWidget* parent = nullptr) : QWidget(parent) {
        button = new QPushButton("Click me", this); // this 是 button 的父对象
    }
    ~MyWidget() {
        // 不需要显式删除 button,父对象会自动销毁所有子对象
    }

private:
    QPushButton* button;
};

引起内存泄漏常见的原因以及解决办法

(1)在类的构造函数和析构函数中没有匹配的调用new和delete函数

  两种情况下会出现这种内存泄露:

  1)在堆里创建了对象占用了内存,但是没有显示地释放对象占用的内存;

  2)在类的构造函数中动态的分配了内存,但是在析构函数中没有释放内存或者没有正确的释放内存。

例子

#include <QApplication>
#include <QLabel>
 
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QLabel *label = new QLabel("Hello Qt!");
    label->show();
    return a.exec();
}

分析:label 既没有指定 parent,也没有对其调用 delete,所以会造成内存泄漏

解决方法:

#include <QApplication>
#include <QLabel>
 
//方法一:对象创建到栈上而不是堆上
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QLabel label("Hello Qt!");
    label.show();
    return a.exec();
}


//方法二:设置标志位,close后会delete label(注意只有分配到栈上才能这样用,要不然就delete栈空间了)
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QLabel *label = new QLabel("Hello Qt!");
    label->setAttribute(Qt::WA_DeleteOnClose);
    label->show();
    return a.exec();
}

//方法三:手动释放
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    int ret = 0;
    QLabel *label = new QLabel("Hello Qt!");
    label->show();
    ret = a.exec();
    delete label;
    return ret;
}

(2)delete栈空间

例子

#include <QApplication>
#include <QLabel>
int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    
    QLabel label("Hello Qt!");
    label.show();
    label.setAttribute(Qt::WA_DeleteOnClose);
    
    return app.exec();
}

分析

程序崩溃,因为 label 被 close 时,delete &label; 但 label 对象是在栈上分配的内存空间,delete 栈上的地址会出错。

有些朋友理解为 label 被 delete 两次而错误,可以测试QLabel label("Hello Qt!"); label.show();delete &label;第一次 delete 就会出错。

解决方法

#include <QApplication>
#include <QLabel>
int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    
    QLabel label("Hello Qt!");
    label.show();
    //注释掉
    //label.setAttribute(Qt::WA_DeleteOnClose);
    
    return app.exec();
}

(3)子对象先于父对象创建

例子

#include <QApplication>
#include <QLabel>
int main(int argc, char* argv[])
{
   QApplication app(argc, argv);
    
   QLabel label("Hello Qt!");
   QWidget w;
   label.setParent(&w);
   w.show();
    
   return app.exec();
}

分析

w 比 label 先被析构,当 w 被析构时,会删除 chilren 列表中的对象 label,但 label 是分配到栈上的,因 delete 栈上的对象而出错。

解决方法

#include <QApplication>
#include <QLabel>
//方法一:交换父子对象创建顺序
int main(int argc, char* argv[])
{
   QApplication app(argc, argv);
    
   QWidget w;
   QLabel label("Hello Qt!");
   label.setParent(&w);
   w.show();
    
   return app.exec();
}

//方法二:将子对象创建到堆上
int main(int argc, char* argv[])
{
   QApplication app(argc, argv);
    
   QWidget w;
   QLabel *label = new QLabel("Hello Qt!");
   label->setParent(&w);
   w.show();
    
   return app.exec();
}

(4)没有正确地清除嵌套的对象指针

例子

#include <QWidget>
#include <QPushButton>

class MyWidget : public QWidget {
public:
    MyWidget() {
        button = new QPushButton("Click Me", this);
        // 错误:没有删除 button
    }
    ~MyWidget() {
        // 忘记删除 button
    }
private:
    QPushButton* button;
};

分析

虽然 QPushButtonMyWidget 作为父对象管理,但在 MyWidget 中声明 QPushButton* button 并在构造函数中使用 new 分配内存时,应该在析构函数中显式地释放 button,以避免潜在的资源泄漏。

解决方法

#include <QWidget>
#include <QPushButton>

//方法一:在析构函数中添加delete
class MyWidget : public QWidget {
public:
    MyWidget() {
        button = new QPushButton("Click Me", this);
    }
    ~MyWidget() {
        // 正确:删除 button
        delete button;
    }
private:
    QPushButton* button;
};

(3)在释放对象数组时在delete中没有使用方括号

方括号是告诉编译器这个指针指向的是一个对象数组,同时也告诉编译器正确的对象地址值并调用对象的析构函数,如果没有方括号,那么这个指针就被默认为只指向一个对象,对象数组中的其他对象的析构函数就不会被调用,结果造成了内存泄露。如果在方括号中间放了一个比对象数组大小还大的数字,那么编译器就会调用无效对象(内存溢出)的析构函数,会造成堆的奔溃。如果方括号中间的数字值比对象数组的大小小的话,编译器就不能调用足够多个析构函数,结果会造成内存泄露。释放单个对象、单个基本数据类型的变量或者是基本数据类型的数组不需要大小参数,释放定义了析构函数的对象数组才需要大小参数。

例子

#include <QWidget>

class MyWidget : public QWidget {
public:
    MyWidget() {
        dataArray = new int[10];
    }
    ~MyWidget() {
        delete dataArray; // 错误:没有使用方括号
    }
private:
    int* dataArray;
};

分析

只会释放第一个对象,对象数组中的其他对象的析构函数就不会被调用,结果造成了内存泄露

解决方法

#include <QWidget>

class MyWidget : public QWidget {
public:
    MyWidget() {
        dataArray = new int[10];
    }
    ~MyWidget() {
        delete[] dataArray; // 正确:使用方括号
    }
private:
    int* dataArray;
};

(4)指向对象的指针数组不等同于对象数组

  对象数组是指:数组中存放的是对象,只需要delete [ ] p,即可调用对象数组中的每个对象的析构函数释放空间

  指向对象的指针数组是指:数组中存放的是指向对象的指针,不仅要释放每个对象的空间,还要释放每个指针的空间,delete [ ] p只是释放了每个指针,但是并没有释放对象的空间,正确的做法,是通过一个循环,将每个对象释放了,然后再把指针释放了。

例子

#include <QWidget>

class MyObject {
public:
    MyObject() {}
    ~MyObject() {}
};

int main() {
    MyObject* objectArray[10];
    for (int i = 0; i < 10; ++i) {
        objectArray[i] = new MyObject();
    }

    // 错误:只释放了指针数组的内存,没有释放对象
    delete[] objectArray;
}

分析

delete [ ] objectArray只是释放了每个指针,但是并没有释放对象的空间

解决方法

#include <QWidget>

class MyObject {
public:
    MyObject() {}
    ~MyObject() {}
};

int main() {
    MyObject* objectArray[10];
    for (int i = 0; i < 10; ++i) {
        objectArray[i] = new MyObject();
    }

    // 正确:先删除每个对象,然后删除指针数组
    for (int i = 0; i < 10; ++i) {
        delete objectArray[i];
    }
}

(5)缺少拷贝构造函数

两次释放相同的内存是一种错误的做法,同时可能会造成堆的奔溃。按值传递会调用(拷贝)构造函数,引用传递不会调用。

在C++中,如果没有定义拷贝构造函数,那么编译器就会调用默认的拷贝构造函数(值拷贝,浅拷贝),复制结果就是两个对象拥有指向同一个动态分配的内存空间的指针。当释放第一个对象的时候,它的析构函数就会释放与该对象有关的动态分配的内存空间。而释放第二个对象的时候,它的析构函数会释放相同的内存,这样是错误的。

所以,如果一个类里面有指针成员变量,要么重写写拷贝构造函数(深拷贝)和重载赋值运算符,要么禁用拷贝构造函数和重载赋值运算符。

例子

#include <QWidget>

class MyObject {
public:
    MyObject() {
        data = new int[100];
    }
    ~MyObject() {
        delete[] data;
    }
private:
    int* data;
};

int main() {
    MyObject obj1;
    MyObject obj2 = obj1; // 错误:隐式拷贝构造函数导致两个对象指向同一块内存
}

分析

隐式拷贝构造函数导致两个对象指向同一块内存,main函数结束时,对象会释放两次

解决方法

#include <QWidget>

class MyObject {
public:
    MyObject() {
        data = new int[100];
    }
    ~MyObject() {
        delete[] data;
    }
    //重写拷贝构造函数,使其为深拷贝
    MyObject(const MyObject& other) {
        data = new int[100];
        std::copy(other.data, other.data + 100, data);
    }
    MyObject& operator=(const MyObject& other) {
        if (this != &other) {
            delete[] data;
            data = new int[100];
            std::copy(other.data, other.data + 100, data);
        }
        return *this;
    }
private:
    int* data;
};

(6)缺少重载赋值运算符

  这种问题跟上述问题类似,也是逐个成员拷贝的方式复制对象,如果这个类的大小是可变的,那么结果就是造成内存泄露.

例子

#include <QWidget>

class MyObject {
public:
    MyObject() {
        data = new int[100];
    }
    ~MyObject() {
        delete[] data;
    }
private:
    int* data;
};

int main() {
    MyObject obj1;
    MyObject obj2;
    obj2 = obj1; // 错误:隐式赋值运算符导致两对象共享相同内存
}

分析

隐式拷贝构造函数导致两个对象指向同一块内存,main函数结束时,对象会释放两次

解决方法

#include <QWidget>

class MyObject {
public:
    MyObject() {
        data = new int[100];
    }
    ~MyObject() {
        delete[] data;
    }
    MyObject& operator=(const MyObject& other) {
        if (this != &other) {
            delete[] data;
            data = new int[100];
            std::copy(other.data, other.data + 100, data);
        }
        return *this;
    }
private:
    int* data;
};

(7)关于nonmodifying运算符重载的常见错误

  1)返回栈上对象的引用或者指针(也即返回局部对象的引用或者指针)。导致最后返回的是一个空引用或者空指针,因此变成野指针(指向被释放的或者访问受限内存的指针);

  2)返回内部静态对象的引用;

  3)返回一个泄露内存的动态分配的对象。导致内存泄露,并且无法回收。

解决这一类问题的办法是重载运算符函数的返回值不是类型的引用,而应该是类型的返回值,即不是 int&而是int。

例子

class MyObject {
public:
    int value;
    MyObject(int v) : value(v) {}
    int& operator++() {
        return value; // 错误:返回栈上对象的引用
    }
};

分析

因为栈上的对象自己会释放,当你访问栈上的对象或者数据时可能访问的不是你所需要的数据

解决方法

class MyObject {
public:
    int value;
    MyObject(int v) : value(v) {}
    int operator++() {
        return value; 
    }
};

(8)没有将基类的析构函数定义为虚函数

  当基类指针指向子类对象时,如果基类的析构函数不是虚函数,那么子类的析构函数将不会被调用,子类的资源没有正确是释放,因此造成内存泄露。

造成野指针的原因:

  1)指针变量没有被初始化(如果值不定,可以初始化为NULL);

  2)指针被free或者delete后,没有置为NULL, free和delete只是把指针所指向的内存给释放掉,并没有把指针本身干掉,此时指针指向的是“垃圾”内存。释放后的指针应该被置为NULL;

  3)指针操作超越了变量的作用范围,比如返回指向栈内存的指针就是野指针;

  4)shared_ptr循环引用。

例子

class Base {
public:
    Base() {}
    ~Base() {} // 错误:未定义为虚函数
};

class Derived : public Base {
public:
    Derived() {}
    ~Derived() {} // 子类的析构函数不会被调用
};

分析

解决方法

class Base {
public:
    Base() {}
    virtual ~Base() {} // 正确:基类的析构函数为虚函数
};

class Derived : public Base {
public:
    Derived() {}
    ~Derived() {}
};

(9)析构的时候使用void*

  delete掉一个void*类型的指针,导致没有调用到对象的析构函数,析构的所有清理工作都没有去执行从而导致内存的泄露。

例子

#include <iostream>

class MyClass {
public:
    MyClass() {
        data = new int[10]; // 分配内存
    }
    ~MyClass() {
        delete[] data; // 释放内存
    }
private:
    int* data;
};

int main() {
    MyClass* obj = new MyClass(); // 创建对象

    void* ptr = static_cast<void*>(obj); // 错误:将 MyClass 对象转换为 void*

    delete static_cast<MyClass*>(ptr); // 错误:使用 void* 删除,析构函数不会被调用

    return 0;
}

分析

创建了一个 MyClass 对象,并将其指针转换为 void*。使用 void* 指针来执行 delete 操作。由于 void* 不包含类型信息,编译器无法调用正确的析构函数,MyClass 对象的析构函数不会被调用,从而导致内存泄漏。

解决方法

程序异常崩溃(持续更新)

参考:

Qt浅谈之一:内存泄露(总结)_qprocess内存泄露-CSDN博客

C++ 内存管理中内存泄漏问题产生原因以及解决方法 - Jcpeng_std - 博客园 (cnblogs.com)

Qt 难找的意料之外原因、崩溃原因及错误原因(持续更新)_qt designer中总是闪退该怎么办-CSDN博客

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

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

相关文章

ZBrush入门使用介绍——8、模型网格显示隐藏和遮罩操作

大家好&#xff0c;我是阿赵。   有时候我们需要把需要雕刻的范围限制在某个局部&#xff0c;之前也介绍过一些方法&#xff0c;比如使用遮罩。这里再详细说一下具体的操作。这次我拿这个圆柱为例子&#xff0c;先生成多边形网格&#xff0c;再CtrlD几次增加一点细分级别。 …

【css】使用!important提升选择器的优先级

背景 昨天我的个人博客备案通过了嘛&#xff0c;然后我就想着完善页面底部的备案信息&#xff0c;参考Argon主题博客美化的 网站底部信息 但是我想要把icp备案和公安联网备案的信息分开&#xff0c;即 subject-value-value 的结构&#xff0c; 因为 value 的选择器里面写的是…

ThinkPHP教程

thinkPHP笔记 01. phpEnv配置安装 主讲老师 - 李炎恢 1. 学习基础 ThinkPHP8.x: 前端基础:HTML5/CSS(必须)、JavaScript(可选、但推荐有);后端基础:PHP基础,版本不限,但不能太老,至少PHP5.4以上语法,TP8是兼容PHP8.x的;数据库基础:MySQL数据库,掌握了常规的SQL…

uni-app总结

1. <u-form-item label"报废人" ><u--input v-model"model.remark" border"bottom" placeholder"请输入"></u--input> </u-form-item> border"bottom" 报废日期 为了

【海贼王航海日志:前端技术探索】一篇文章带你走进JavaScript(一)

目录 1 -> 初识JavaScript 1.1 -> JavaScript是什么 1.2 -> 发展历史 1.3 -> JavaScript和HTML和CSS之间的关系 1.4 -> JavaScript运行过程 1.5 -> JavaScript的组成 2 -> 前置知识 2.1 -> JavaScript的书写形式 2.2 -> 注释 2.3 -> 输…

Stable Diffusion-inpaint(mask补全)是怎么做的?

AIGC专栏4——Stable Diffusion原理解析-inpaint修复图片为例_diffusion inpaint-CSDN博客 如果我们必须训练一个inpaint模型才能对当前的模型进行inpaint&#xff0c;那就太麻烦了&#xff0c;有没有什么方法可以不需要训练就能inpaint呢&#xff1f; Stable Diffusion就是一…

Unity新输入系统 之 InputAction(输入配置文件最基本的单位)

本文仅作笔记学习和分享&#xff0c;不用做任何商业用途 本文包括但不限于unity官方手册&#xff0c;unity唐老狮等教程知识&#xff0c;如有不足还请斧正​ 首先你应该了解新输入系统的构成结构&#xff1a;Unity新输入系统结构概览-CSDN博客 Input System - Unity 手册 1.In…

创客匠人媛姐:做得一切都是为了拿到结果!

大家好&#xff0c;我是媛姐。近期我做了《百场IP发售销讲实战宣讲-发售教练点评》的直播活动。邀请了艺得世界人才创造社白钰玮老师为大家现场演练一场销讲&#xff0c;展示发售销讲私教班的培训成果。结果证明&#xff0c;白老师完成得非常出色。 以下&#xff0c;我将分享一…

WPF APP生命周期和全局异常捕获

应用启动事件与启动参数 属性查找 选择想要控件的事件&#xff0c;可以在控件上鼠标右击选择属性&#xff0c;在右上角点击闪电符号即可看到这个控件的所有事件&#xff1a; APP.Run()启动方法&#xff1a; 打开项目中这个文件&#xff1a; ".....\XH.EventLesson\obj…

Chapter 9 Operational Amplifiers

Chapter 9 Operational Amplifiers operational amplifier (op-amp) 运算放大器无疑是模拟电路中最基础最重要的block之一. 这一章我们首先review telescopic and folded-cascode 拓扑, 然后学习two-stage and gain-boosting 结构, 和共模反馈问题, 最后引入slew rate, 分析su…

基于Python、Django开发Web计算器

1、创建项目 创建Django项目参照https://blog.csdn.net/qq_42148307/article/details/140798249&#xff0c;其中项目名为compute&#xff0c;并在该项目下创建一个名为app的应用&#xff0c;并且进行基本的配置。 2、导入Bootstrap前端框架 Bootstrap的使用参照https://blo…

【项目分享】使用python的ttkbootstrap模块构建一个炫酷的计时器

目录 前言 项目背景 项目展示(图片) 项目实现 1. 安装与设置 2. 创建主窗口 3. 初始化计时器功能 4. 实现计时功能 5. 实现隐藏边框与置顶功能 6. 运行应用 完整代码 结论 🌟 嗨,我是命运之光! 🌍 2024,每日百字,记录时光,感谢有你一路同行。 🚀 携…

TCP详解(二)滑动窗口/流量控制

本文解释了TCP为何能保证数据传输的可靠性&#xff0c;以及如何保证整个网络的顺畅。 1 网络分层模型 这是一切的本质。网络被设计成分层的&#xff0c;所以网络的操作就可以称作一个“栈”&#xff0c;这就是网络协议栈的名称的由来。在具体的操作上&#xff0c;数据包最终形…

tcpdump入门——抓取三次握手数据包

1. 使用docker启动一个tcp应用 参考&#xff1a;https://blog.csdn.net/LONG_Yi_1994/article/details/141175526 2. 获取容器id docker ps |grep gochat 3. 获取容器的 PID 首先&#xff0c;你需要获得容器的进程 ID&#xff08;PID&#xff09;。可以使用 docker inspect…

kafka下载|安装

1、下载kafka https://kafka.apache.org/downloads 2、安装kafka 解压下载的kafka安装包即可 tar -xvf kafka_2.13-3.7.0.tgz -C /usr/local/3、查看kafka目录 bin目录&#xff1a;存放了脚本 config目录&#xff1a;主要存放了配置文件

Pytest-BDD流程性接口测试和自定义测试报告

引言 上篇文章《Pytest-BDD实现接口自动化测试&#xff0c;并附全部代码》我们介绍了怎么使用Pytest-BDD实现接口自动化测试&#xff0c;本篇文章主要介绍怎么去做流程性接口测试和自定义测试报告相关内容。 流程性接口测试 流程性接口测试&#xff0c;指的是一个业务流需要…

【九芯电子】智能声控台灯语音模块,低成本语音识别芯片

在当今数字化时代&#xff0c;智能家居已经逐渐成为现代生活中的一部分。从温度调节到安全监控&#xff0c;我们对家居设备的控制已经更加便捷。然而&#xff0c;随着生活节奏的加快&#xff0c;用户对于更便捷的家庭控制方式的需求也在不断增加。针对这一关键的问题&#xff0…

HBO引爆血腥浪漫,尺度全开必看的影视剧推荐

一直以来我们的僵尸题材电影风靡全国&#xff0c;同时西方也创作出吸血鬼题材、丧尸题材的影视剧也是层出不穷&#xff0c;那今天我们就来探讨下吸血鬼题材的影视剧。 吸血鬼题材的影视剧&#xff0c;一直以来都是观众的宠儿。从光鲜亮丽的《暮光之城》到狗血多角恋的《吸血鬼日…

河北移动:核心系统数据库成功完成整体迁移 ,实现全栈国产|OceanBase案例

本文作者&#xff1a;移动通信集团河北有限公司架构规划专家&#xff0c;房瑞 项目背景&#xff1a; 中国移动通信集团河北有限公司一直在积极响应国家及集团的号召&#xff0c;以磐舟&磐基云原生为底座&#xff0c;结合国产浏览器、中间件、数据库、操作系统和服务器等&a…

树莓派4b无法选择声音输入输出设备问题

问题一&#xff1a;选择不了3.5mm音频输出口&#xff0c;也看不到音频输入设备 运行命令 &#xff1a;pactl load-module module-udev-detect tsched0 pactl load-module 命令用于在运行时加载新的模块。module-udev-detect 是PulseAudio的一个模块&#xff0c;它负责自动检测系…