Linux进程间通信

进程间通信介绍

首先进程是具有独立性的,要让两个不同的进程,进行通信,前提是:先让两个进程,看到同一份资源,这份资源及不能属于进程A也不能属于进程B,所以只能有操作系统直接或间接提供,然后让一方写入,一方读取完成通信过程,至于通信的目的与后续工作,要结合后续具体场景。

管道

首先Linu下一切皆文件,管道也一样。管道文件也有缓冲区,和磁盘文件一样。但是管道是一个操作系统提供的内存文件,它并不需要再将自己的有效内容刷新到磁盘当中,这种文件也称之为匿名文件。最后我们关闭它操作系统会直接把结构释放掉。

匿名管道

在这里插入图片描述

站在文件描述符角度-深度理解管道

  1. 父进程创建管道
    在这里插入图片描述
  2. 父进程fork出子进程
    在这里插入图片描述
  3. 父进程关闭fd[0],子进程关闭fd[1]
    在这里插入图片描述

父进程以读和写的方式打开文件,那么fork之后它的读端和写端都能够被子进程继承下去。然后形成通信的信道,那么我们要形成单向通信的信道,就必须得关闭特定的读写端。

#include <iostream>
#include <string>
#include <errno.h>
#include <assert.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
int main()
{
    //让不同的进程看到同一份资源
    //任何一种进程间通信,一定要先保证不同的进程看到同一份资源
    int pipefd[2] = {0};
    //1.创建管道
    int n = pipe(pipefd);
    if(n == -1)
    {
        std::cout << "pipe调用失败," << errno << ":" << strerror(errno)<< std::endl;
        return 1;
    }
    std::cout << "pipefd[0]" << ":" << pipefd[0]<< std::endl;//读端
    std::cout << "pipefd[1]" << ":" << pipefd[1]<< std::endl;//写端
    //2.创建子进程
    pid_t id = fork();
    if(id == -1)
    {
        std::cout << "fork失败," << errno << ":" << strerror(errno)<< std::endl;
        return 1;
    }
    if(id == 0)
    {
        //子进程
        //3.关闭不需要的fd,让父进程读取,让子进程写入
        close(pipefd[0]);
        //4.开始通信---结合具体场景
        std::string namestr = "hello,我是子进程";
        int cnt = 1;
        char buffer[1024] = {0};
        while(true)
        {
            snprintf(buffer,sizeof buffer,"%s,计数器:%d,我的PID: %d\n",namestr.c_str(), ++cnt, getpid());
            write(pipefd[1],buffer,strlen(buffer));
            sleep(1);
        }

        close(pipefd[1]);
        exit(0);
    }
    //父进程
    //3.关闭不需要的fd,让父进程读取,让子进程写入
    close(pipefd[1]);
    //4.开始通信---结合具体场景
    char buffer[1024] = {0};
    while(true)
    {
        int n = read(pipefd[0],buffer,sizeof(buffer) - 1);
        if(n > 0)
        {
            buffer[n] = '\0';
            std::cout<< "我是父进程,读取的内容是:"<< buffer << std::endl;
        }
        else if(n == 0)//表示read读取到了文件结尾了
        {
            std::cout<<"我是父进程,读取到文件结尾"<<std::endl;
            break;
        }
        else // read返回-1,表示出错了
        {
            std::cout<<"我是父进程,读取出错了"<<std::endl;
            break;
        }
    }
    close(pipefd[0]);
    int status = 0;
    waitpid(id, &status, 0);
    std::cout << "sig: " << (status & 0x7F) << std::endl;
    return 0;
}

管道的特点:

  • 单向通信,如果想双向通信就在加一个管道。管道是半双工的。
  • 管道的本质是文件,因为文件描述的生命周期随进程,所以管道的生命周期是随进程的。
  • 管道通信,通常用来进行具有"血缘”关系的进程,进行进程间通信。常用与父子通信—原理还是fork之后的继承。pipe打开的管道,并不清楚管道的名称,所以称为匿名管道。
  • 在管道通信中,写入的次数,和读取的次数,不是严格匹配的读写次数的多少没有强相关----表现字节流。
  • 具有一定的协同能力,让reader和writer能够按照一-定的步骤进行通信-----自带同步机制。

管道的几种特殊场景

  • 如果read读取完了管道内所有数据,如果对方不写入,就只能等待。
  • 如果writer端将管道写满了,管道也是文件,文件就有固定大小,所以就不能写了。
  • 如果关闭了写端,读取完毕管道数据,在读就会read返回0,表示读到了文件结尾。
  • 写端一直写,读端关闭,OS不会维护无意义,低效率,或者浪费资源的事情。OS会杀死一直在写入的进程。OS会通过信号来终止进程。13号信号。
  • 当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。
  • 当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性

注意:
在管道中写入的数据,只要读取了,那么读取过的数据就会被 “删除”。这里的删除,实际上是这些数据被读取过了,它就无效了,意味着下次在写入的时候可以拷贝覆盖。

命名管道

  • 管道应用的一个限制就是只能在具有共同祖先(具有亲缘关系)的进程间通信。
  • 如果我们想在不相关的进程之间交换数据,可以使用FIFO文件来做这项工作,它经常被称为命名管道。
  • 命名管道是一种特殊类型的文件,命名管道是内存级文件,不会刷盘。

当两个不同的进程,打开同一个文件时,操作系统不会维护两个一样的结构体(struct file)。当新进程想打开其他文件,新进程做的第一件事情,是在所有已经打开的文件列表里去找这个文件是否已经被打开了,如果没有被打开,就创建一个结构体(struct file),如果打开了他就直接把对应的结构体(struct file)对象的地址填入到当前进程的文件描述符表里,而对应的struct file里面它包含一个叫做int ret的引用计数器就会加加。

命令行上创建命名管道

命名管道可以从命令行上创建,命令行方法是使用下面这个命令:

mkfifo fifo

在这里插入图片描述

函数创建命名管道

命名管道也可以从程序里创建,相关函数有:
在这里插入图片描述
comm.hpp

#pragma once
#include <iostream>
#include <string>
#define NUM 1024

const std::string fifoname = "./fifo";//要创建的文件名
uint32_t mode = 0666; //创建的文件的权限

server.cc

#include <sys/types.h>
#include <sys/stat.h>
#include <iostream>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#include "comm.hpp"
int main()
{
    // 1. 创建管道文件,我们今天只需要一次创建
    umask(0);//将当前进程的umask 设置为0,这个设置并不影响系统的默认配置,只会影响当前进程
    int n = mkfifo(fifoname.c_str(),mode);
    if(n == -1)
    {
        std::cerr << "mkfifo 创建命名管道失败" << errno << " : " << strerror(errno)<< std::endl;
        exit(-1);
    }
    std::cout << "创建 fifo 文件成功" << std::endl;
    // 2. 让服务端直接开启管道文件
    int fd = open(fifoname.c_str(),  O_RDONLY);
    if(fd == -1 )
    {
        std::cerr << "open 打开文件失败" << errno << " : " << strerror(errno) << std::endl;
        return 2;
    }
    std::cout << "打开fifo成功, 开始通信" << std::endl;
    // 3. 正常通信
    char buffer[NUM]= {0};
    while(true)
    {
        buffer[0] = 0;
        ssize_t r = read(fd, buffer, sizeof(buffer) - 1);
        if(r > 0)
        {
            buffer[r] = 0;
            std::cout << "client# " << buffer << std::endl;
        }
        else if( r == 0)
        {
            std::cout<< "读取到文件结束" << std::endl;
            break;
        }
        else
        {
            std::cerr <<"文件读取错误:" <<errno << " : " << strerror(errno) << std::endl;
            break;
        }
    }
    // 关闭不要的fd
    close(fd);

    unlink(fifoname.c_str());//删除创建的管道文件
   return 0;
}

client.cc

#include <iostream>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#include "comm.hpp"
int main()
{
    //1. 不需创建管道文件,我只需要打开对应的文件即可!
    int fd = open(fifoname.c_str(), O_WRONLY);
    if(fd == -1 )
    {
        std::cerr << "open 打开文件失败" << errno << " : " << strerror(errno) << std::endl;
        return 2;
    }

    // 可以进行常规通信了
    char buffer[NUM];
    while(true)
    {
        std::cout << "请输入你的消息# ";
        char *msg = fgets(buffer, sizeof(buffer), stdin);
        buffer[strlen(buffer) - 1] = 0;
        // abcde\n\0
        // 012345
        if(strcasecmp(buffer, "quit") == 0) break;

        ssize_t n = write(fd, buffer, strlen(buffer));
        
    }
    close(fd);
    return 0;
}

运行结果
在这里插入图片描述
在这里插入图片描述
匿名管道与命名管道的区别

  • 匿名管道由pipe函数创建并打开。
  • 命名管道由mkfifo函数创建,打开用open
  • FIFO(命名管道)与pipe(匿名管道)之间唯一的区别在它们创建与打开的方式不同,一但这些工作完成之后,它们具有相同的语义

system V共享内存

共享内存的原理

先创建出一块物理内存,然后再通过一定的接口将物理内存的地址映射到我们两个要通信进程,各自的地址空间当中,那么最终我们返回地理空间时。进程A和进程B,它们使用时直接就用的是虚拟地址,经过页表转化不就可以访问到我们的物理内存当中特定的同一块内存了吗?所以进程间通信的前提,我们一直在强调,叫做让不同的进程得先看到同一份资源,那么这份同样的资源,是一个内存块,那么我们把它称之为共享内存。
在这里插入图片描述
注意:匿名管道和命名管道是通过文件来实现通信的,共享内存是通过内存块让不同的进程实现通信。共享内存允许多个进程进行通信。

共享内存函数

shmget

在这里插入图片描述

创建方式说明
IPC_CREAT创建一个共享内存,如果共享内存不存在,就创建之,如果已经存在,获取已经存在的共享内存并返回
IPC_EXCL不能单独使用,一般都要配合IPC_CREAT
IPC_CREAT l IPC_EXCL创建一个共享内存,如果共享内存不存在,就创建之, 如果已经存在,则立马出错返回 ---- 如果创建成功,对应的共享内存(shmget的返回值),一定是最新的!
ftok

在这里插入图片描述
在这里插入图片描述

在Linux中,ipcs查看进程间通信方式的信息,包括共享内存,消息队列,信号。可以携带一下选项

  • q: 只查看系统消息队列信息
  • m: 只查看系统共享内存信息
  • s: 只查看系统信号量信息
ipcs -m命令的查看共享内存的列头解释
key操作系统识别共享内存的唯一标识
shmid用户层识别共享内存的id标识
owner共享内存的拥有者
perms共享内存的权限
bytes共享内存的大小
nattch关联共享内存的进程数
status共享内存的状态

shmat

在这里插入图片描述

shmdt

在这里插入图片描述

shmctl

在这里插入图片描述

cmd的三种权限说明
IPC_STAT把shmid_ds结构中的数据设置为共享内存的当前关联值
IPC_SET在进程有足够权限的前提下,把共享内存的当前关联值设置为shmid ds数据结构中给出的值
IPC_RMID删除共享内存段

指令ipcrm -m shmid释放共享内存为shmid的内存。

comm.hpp

#ifndef __COMM_HPP__
#define __COMM_HPP__
#include <iostream>
#include <sys/types.h>
#include <sys/ipc.h>
#include <string.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <assert.h>
#include <unistd.h>

#define PATHNAME "."
#define PROJID 0x6667

//共享内存的大小是以PAGE页(4KB)为单位的,开辟空间时以4KB为单位开辟。
const int gsize = 4096; //暂时
key_t getFtok()
{
    key_t k = ftok(PATHNAME, PROJID);//保证生成的值是一样的。
    if(-1 == k)
    {
        std::cerr << "调用ftok函数失败" << "error: " << errno << " : " << strerror(errno) << std::endl;
        exit(1);
    }
    return k;
}

std::string toHex(int x)
{
    char buffer[64] = {0};
    snprintf(buffer, sizeof(buffer), "0x%x", x);
    return buffer;
}

static int createShmHelper(key_t k, int size, int flag)
{
    int shmid = shmget(k, size, flag);
    if(-1 == shmid)
    {
        std::cerr << "调用shmid函数失败" << "error: " << errno << " : " << strerror(errno) << std::endl;
        exit(2);
    }

    return shmid;
}
//创建共享内存
int createShm(key_t k, int size)
{
    umask(0);
    //IPC_CREAT | IPC_EXCL | 0666 创建新的共享内存并将共享内存设置权限(这里的权限与文件权限一样)
    return createShmHelper(k, size, IPC_CREAT | IPC_EXCL | 0666);
    
}
//获取共享内存
int getShm(key_t k, int size)
{
    return createShmHelper(k, size, IPC_CREAT);
}

//链接共享内存
char* attachShm(int shmid)
{
    char *start = (char*)shmat(shmid,nullptr,0);
    //shmat 与 malloc 的
    return start;
}
//脱离共享内存的链接
void detachShm(void *start)
{
    int n = shmdt(start);
    assert(n != -1);
    (void)n;
}
//删除共享内存
//1. 函数删除
//2. 指令删除
void delShm(int shmid)
{
    //shmid_ds * temp; 如果你想看这个结构体的话,就定义一个结构体传入就行,不想看设置为nullptr就行
    int n = shmctl(shmid, IPC_RMID, nullptr);
    assert(n != -1);
    (void)n;
}

#define SERVER 1
#define CLIENT 0

struct Init
{
public:
    Init(int type)
        :_type(type)
    {
        key_t k = getFtok();
        if(type == SERVER)
        {
            shmid = createShm(k,gsize);
        }
        else
        {
            shmid = getShm(k, gsize);
        }
       start = attachShm(shmid);
    }
    char *getStart()
    { 
        return (char*)start; 
    }

    ~Init()
    {
        detachShm(start);
        if(_type == SERVER) 
            delShm(shmid);
    }
private:
    int shmid;
    void* start;
    int _type; //server or client
};
#endif

shmserver.cc

#include "comm.hpp"
int main()
{
    // //1. 创建key
    // key_t k = getFtok();
    // std::cout << "server key: " << toHex(k) << std::endl;

    // //2. 创建共享内存
    // int shmid = createShm(k, gsize);
    // std::cout << "server shmid: " << shmid << std::endl;

    // char *start = attachShm(shmid);
    // sleep(15);
    // detachShm((void*)start);

    // delShm(shmid);

    Init init(SERVER);

    // start 就已经执行了共享内存的起始空间
    char *start = init.getStart();

    int n = 0;
    // 我们在通信的时候,没有使用任何接口?一旦共享内存映射到进程的地址空间,该共享内存就直接被所有的进程 直接看到了!
    // 因为共享内存的这种特性,可以让进程通信的时候,减少拷贝次数,所以共享内存是所有进程间通信,速度最快的
    // 共享内存没有任何的保护机制(同步互斥) -- 为什么?管道通过系统接口通信,共享内存直接通信
    while(n <= 30)
    {
        std::cout <<"client -> server# "<< start << std::endl;
        sleep(1);
        n++;
    }
    return 0;
}

shmclient.cc

#include "comm.hpp"
int main()
{
    // key_t k = getFtok();
    // std::cout << "client key: " << toHex(k) << std::endl;

    // int shmid = getShm(k, gsize);
    // std::cout << "client shmid: " << shmid << std::endl;
    // char *start = attachShm(shmid);
    // sleep(10);
    // detachShm((void*)start);

    Init init(CLIENT);
    // start 就已经执行了共享内存的起始空间
    char *start = init.getStart();
    char c = 'A';

    while(c <= 'Z')
    {
        start[c - 'A'] = c;
        c++;
        start[c - 'A'] = '\0';
        sleep(1);
    }
    
    return 0;
}

共享内存与管道文件通信速度的比较

在这里插入图片描述
在考虑外设的情况下,首先是管道文件要经历4次拷贝,进程从外设(键盘,网络)中读取到数据,写入到语言(C, C++,…)缓冲区中(一次拷贝),按语言缓冲区的刷新策略,刷新到管道文件中(二次拷贝),然后另一进程再将管道文件中的数据拷贝到语言(C, C++,…)缓冲区中(三次拷贝),在拷贝到外设中进行读取(四次拷贝)。其次是共享内存要经历2次拷贝,进程从外设(键盘,网络)中读取到数据写入到虚拟地址空间,虚拟地会通过页表进行映射到物理地址空间完成拷贝(一次拷贝),然后另一进程直接进行读取就行,在这之中虚拟地会通过页表进行映射到物理地址空间,找到数据,然后将数据拷贝到外设中(二次拷贝)。

共享内存和管道不一样,如果写端没有写,读端可以一直读。共享内存在并发访问的时候没有进行任何保护。共享内存了,没有同步和互斥

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

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

相关文章

OkHttpUrlConnection库编写代码示例

OkHttpUrlConnection库编写的爬虫程序&#xff0c;该程序使用Kotlin编写的。 kotlin import java.net.HttpURLConnection import java.net.URL import java.net.URLConnection import java.io.BufferedReader import java.io.InputStreamReader fun main() { val url UR…

JVM中如何实现垃圾收集

Java虚拟机&#xff08;JVM&#xff09;使用垃圾收集器&#xff08;Garbage Collector&#xff09;来管理内存&#xff0c;清理不再使用的对象以释放内存空间。垃圾收集的主要目标是自动化内存管理&#xff0c;使开发人员无需显式地释放不再使用的内存&#xff0c;从而降低了内…

抖音本地生活服务商申请入口关闭?聚合服务商将成本地生活新模式

近年来&#xff0c;随着抖音本地生活服务为用户提供了便捷的生活方式相继支付宝、微信陆续推出了本地生活服务。然而&#xff0c;对于许多创业者而言&#xff0c;申请成为抖音本地生活服务商却面临着一定的门槛。因此&#xff0c;如何降低这些门槛&#xff0c;让更多的商家能够…

notion 3.0.0 版本最新桌面端汉化教程,支持MAC和WIN版本

notion客户端汉化&#xff08;目前版本3.0.0&#xff09; 最近notion桌面端更新了3.0.0版本后会导致老版本汉化失效&#xff0c;本项目实现了最新版Notion桌面端的汉化。 文件下载地址&#xff1a;汉化文件下载地址 项目说明 本项目针对新的客户端做了汉化文化&#xff0c;依…

ke12Servlet规范有三个高级特性,,文件上传下载

1Servlet规范有三个高级特性 分别是Filter、Listener和文件的上传下载。Filter用于修改request、response对象&#xff0c;Listener用于监听context、session、request事件。 熟悉Filter的生命周期 了解Filter及其相关API 掌握Filter的实现 掌握Filter的映射与过滤器链的使用…

第一个Mybatis项目

&#xff08;一&#xff09;为什么要用Mybatis? &#xff08;1&#xff09;Mybatis对比JDBC而言&#xff0c;sql&#xff08;单独写在xml的配置文件中&#xff09;和java编码分开&#xff0c;功能边界清晰&#xff0c;一个专注业务&#xff0c;一个专注数据。 &#xff08;2&…

java设计模式学习之【工厂模式】

文章目录 引言工厂方法模式简介定义与用途&#xff1a;实现方式&#xff1a; 使用场景优势与劣势工厂模式在spring中的应用电费计算示例&#xff08;简单工厂模式&#xff09;改善为方法工厂模式代码地址 引言 在软件开发的世界中&#xff0c;对象的创建可能是一个复杂且重复的…

【Git】一文教你学会 submodule 的增、删、改、查

添加子模块 $ git submodule add <url> <path>url 为想要添加的子模块路径path 为子模块存放的本地路径 示例&#xff0c;添加 r-tinymaix 为子模块到主仓库 ./sdk/packages/online-packages/r-tinymaix 路径下&#xff0c;命令如下所示&#xff1a; $ git subm…

java 手机商城免费搭建+电商源码+小程序+三级分销+SAAS云平台

【SAAS云平台】打造全行业全渠道全场景的SaaS产品&#xff0c;为店铺经营场景提供一体化解决方案&#xff1b;门店经营区域化、网店经营一体化&#xff0c;本地化、全方位、一站式服务&#xff0c;为多门店提供统一运营解决方案&#xff1b;提供丰富多样的营销玩法覆盖所有经营…

【数据结构/C++】栈和队列_链队列

#include <iostream> using namespace std; // 链队列 typedef int ElemType; typedef struct LinkNode {ElemType data;struct LinkNode *next; } LinkNode; typedef struct {LinkNode *front, *rear; } LinkQueue; // 初始化 void InitQueue(LinkQueue &Q) {Q.fron…

2024免费MacBook清理工具CleanMyMac X4.15

CleanMyMac X 是一款专业的Mac清理软件&#xff0c;可智能清理mac磁盘垃圾和多余语言安装包&#xff0c;快速释放电脑内存&#xff0c;轻松管理和升级 Mac 上的应用。同时 CleanMyMac X 可以强力卸载恶意软件&#xff0c;修复系统漏洞&#xff0c;一键扫描和优化 Mac 系统&…

医院手术麻醉信息系统全套源码,自主版权,支持二次开发

医院手术麻醉信息系统全套商业源码&#xff0c;自主版权&#xff0c;支持二次开发 手术麻醉信息系统是HIS产品的中的一个组成部分&#xff0c;主要应用于医院的麻醉科&#xff0c;属于电子病历类产品。医院麻醉监护的功能覆盖整个手术与麻醉的全过程&#xff0c;包括手术申请与…

Talk | 牛津大学博士后研究员边佳旺:SC-DepthV3-动态场景中的自监督单目深度估计

本期为TechBeat人工智能社区第550期线上Talk。 北京时间11月23日(周四)20:00&#xff0c;牛津大学博士后研究员—边佳旺的Talk已准时在TechBeat人工智能社区开播&#xff01; 他与大家分享的主题是: “SC-DepthV3&#xff1a;动态场景中的自监督单目深度估计”&#xff0c;介绍…

ros2文件package.xml与cmakelists.txt比较

每次在ros2里面添加文件以后&#xff0c;都要修改packages.xml,与cmakelists.txt文件。

ssm+vue的企业文档管理系统(有报告)。Javaee项目,ssm vue前后端分离项目。

演示视频&#xff1a; ssmvue的企业文档管理系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;ssm vue前后端分离项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&…

RabbitMQ 消息队列编程

安装与配置 安装 RabbitMQ 读者可以在 RabbitMQ 官方文档中找到完整的安装教程&#xff1a;Downloading and Installing RabbitMQ — RabbitMQ 本文使用 Docker 的方式部署。 RabbitMQ 社区镜像列表&#xff1a;https://hub.docker.com/_/rabbitmq 创建目录用于映射存储卷…

仿activiti实现一个简版工作流

简版工作流 前言功能实现1.需求及实现2.代码实现1.流程设置2.项目权限设置1.表设计 3.任务流程处理1.表设计2.代码实现 4.任务流程记录5.接口说明 前言 本文代码实现是仿照工作流实现的&#xff0c;由于业务需求的特殊性&#xff0c;没有直接使用工作流。 功能实现 功能实现…

3款免费次数多且功能又强大的国产AI绘画工具

hi&#xff0c;同学们&#xff0c;本期是我们第55 期 AI工具教程 最近两个月&#xff0c;国内很多AI绘画软件被关停&#xff0c;国外绝大部分AI绘画工具费用不低&#xff0c;因此 这两天我 重新整理 国产 AI绘画 工具 &#xff0c; 最终 筛选了 3款功能强大&#xf…

GoLand 2023.2.5(GO语言集成开发工具环境)

GoLand是一款专门为Go语言开发者打造的集成开发环境&#xff08;IDE&#xff09;。它能够提供一系列功能&#xff0c;如代码自动完成、语法高亮、代码格式化、代码重构、代码调试等等&#xff0c;使编写代码更加高效和舒适。 GoLand的特点包括&#xff1a; 1. 智能代码补全&a…

Java常量池理论篇:Class常量池、运行时常量池、String常量池、基本类型常量池,intern方法1.6、1.7的区别

文章目录 Class常量池运行时常量池String常量池基本类型常量池Integer 常量池Long 常量池 加餐部分 Class常量池 每个Class字节码文件中包含类常量池用来存放字面量以及符号引用等信息。 运行时常量池 java文件被编译成class文件之后&#xff0c;也就是会生成我上面所说的 …