文档库 最新最全的文档下载
当前位置:文档库 › 内存的分配与回收

内存的分配与回收

内存的分配与回收
内存的分配与回收

操作系统实验报告

实验名称:模拟内存的分配与回收

文档编写者:吴向阳

系班:计算机科学系0501班

学号:05150116

完成时间:2007年12月23日

指导老师:陈年生李光敏

模拟内存的分配与回收

班级:计算机科学系0501班姓名:吴向阳完成时间:2007年12月13日

问题描述

本实验主要是模拟基于多道程序设计系统在可变分区式和分页式两种存储管理方式下的主存分配和回收。在程序中采用链表方式来管理作业和页表项,采用队列来管理等待作业。在模拟可变分区式存储管理时,采用最先适应算法实现主存的分配与回收,建立了已分配区表和未分配区表,等待队列。在模拟分页式存储管理时,采用位示图来表示主存,建立了作业表和页表,在主存中的作业队列和等待队列。需要说明的是,我们没有考虑系统可容纳作业的道数和作业对除主存以外的其它资源的要求,认为只要系统主存可以满足要求作业就加载到主存。同时等待队列是采用先进先出的管理方式。

数据结构及符号说明

可变分区式存储管理的已分配区表和未分配区表的结构用记录描述如下:struct Assigntab{

int number;//分区号

int addr;//起始地址

int length;//长度

char sign[10];//标志

struct Assigntab* next;//指向下一个分区号

};

分页式存储管理:

位示图记录描述:

int weitu[8][8];//位示图

int blank=64;//当前空闲的块号

作业表的结构用记录描述:

//作业表结构体

struct jobtable{

char jobname[10];//作业的名字

int jobsize;//作业的大小

struct pagetable* head;//指向作业的页表

struct jobtable* next;//指向下一个作业

};

页表的结构用记录描述:

//作业的页表结构体

struct pagetable{

int pagenum;//页号

int blocknum;//块号

struct pagetable* next;//指向下一项

};

流程图

开 始

初 始 化

输入操作码

结 束

打印 未分 配表

打印 已分 配表

打印 等待 队列

分配 内存

回收 内存

0 1 2 3 4 5 主 程 序 该可变分区式存储管理程序由主程序、初始化子程序、打印子程序、模拟内存分配子程序、模拟内存回收子程序等构成,下面给出这些模块的框图:

分页式存储管理程序由主程序、初始化子程序、打印位示图子程序、打印作业表子程序、打印页表子程序、打印等待作业表、模拟内存分配子程序、模拟内存回收子程序等构成。

0 1

开始

初始化输入操作码

结束打印

位示

打印

等待

队列

打印

内存

作业

打印

作业

页表

分配

内存

2 3 4 5

回收

内存

6

主程序

//可变分区式存储管理(最先适应算法)

#include ""

#include ""

#include<>

#define NULL 0

#define LEN sizeof(struct Assigntab)

#define DEBUG

//数据结构的定义

//已分配区表Assigtab,未分配区表Unassigntab,保存一栏的信息temp

//标志sign为NULL时表示该栏为空

//定义两个结构体指针Asshead(指向表Assigtab)和Unahead(指向表Unassigntab) //用全局变量sucessed

struct Assigntab{

int number;//分区号

int addr;//起始地址

int length;//长度

char sign[10];//标志

struct Assigntab* next;//指向下一个分区号

};

struct Assigntab *Asshead,*Unahead,*waithead;

struct Assigntab *p,*q;

struct Assigntab *r1,*s1;

int n=0;

int n1=0;

int n2=0;

int success=0;

//主函数

int main()

{

char ch;

p=q=(struct Assigntab*)malloc(LEN);

r1=s1=(struct Assigntab*)malloc(LEN);

init();

cout<<"请选择操作码:";

cin>>ch;

do{

if(ch=='0') break;

switch(ch)

{

case '1':displayUna();break;//查询未分分配区表

case '2':displayAss();break;//查询已分配区表

case '3':displaywait();break;//查询等待作业队列

case '4':fenpei();break;//装载一个作业

case '5':huishou();break;//撤消一个作业

}

cout<<"请选择操作码:";

cin>>ch;

if(ch=='0') break;

}while(1);

return 0;

}

//分页式存储管理

#include<>

#include<>

#include<>

#include ""

#include ""

#define NULL 0

#define LEN sizeof(struct pagetable)

#define LENJ sizeof(struct jobtable)

//作业的页表结构体

struct pagetable{

int pagenum;//页号

int blocknum;//块号

struct pagetable* next;//指向下一项};

//作业表结构体

struct jobtable{

char jobname[10];//作业的名字

int jobsize;//作业的大小

struct pagetable* head;//指向作业的页表

struct jobtable* next;//指向下一个作业};

int weitu[8][8];//位示图

int weitusize=2;//内存每块为2K

int blank=64;//当前空闲的块号

int sign=0;//内存分配错误标志

int list=0;//作业序列

int n=0;//内存作业队列标志

int n1=0;//等待作业队列标志

struct jobtable *r,*s;

struct jobtable *r1,*s1;

struct jobtable *jobhead;//作业表

struct jobtable *waithead;//等待作业表

//主函数

int main()

{

char ch;

//初始化工作

jobhead=NULL;

waithead=NULL;

initweitu();

r=s=(struct jobtable*)malloc(LENJ);

r1=s1=(struct jobtable*)malloc(LENJ);

//菜单功能

cout<<"**********分页式内存管理的模拟系统************"<

cout<<"查询内存分配位示图,请按:1\n"

<<"查询等待作业队列,请按:2\n"

<<"查询内存中作业队列,请按:3\n"

<<"查询某作业的页表,请按:4\n"

<<"装载一个作业,请按:5\n"

<<"撤消一个作业,请按:6\n"

<<"退出系统,请按:0\n";

cout<<"**********************************************"<

cout<<"请选择操作码:";

cin>>ch;

do{

if(ch=='0') {cout<<"你真的要退出系统吗?(Y/N):";

cin>>ch;

if(ch=='Y') break;

else{cout<<"请选择操作码:";

cin>>ch;}

}

//用switch语句实现任务选择功能

switch(ch)

{

case '1':display();break;

case '2':displaywait();break;

case '3':displayjob();break;

case '4':yebiao();break;

case '5':jiazai();break;

case '6':cexiao();break;

default:cout<<"您的输入有错误,请重新输入\n";

}

cout<<"请选择操作码:";

cin>>ch;

if(ch=='0') {cout<<"你真的要退出系统吗?(Y/N):";

cin>>ch;

if(ch=='Y') break;

else{cout<<"请选择操作码:";

cin>>ch;}

}

}while(1);

cout<<"谢谢使用分页式内存管理的模拟系统"<

return 0;

}

运行结果:

实验说明:

在可变分区管理中,采用最先适应分配算法,这种算法优先利用主存低地址空闲分区不断被分割,既可能将大的空间分割掉,也造成低地址部分有较多难以使用的“碎片“。作为改进,可把空闲区按地址从小到大排列在未分配表或链表中。

主存空间的分配与回收—首次适应法

主存空间的分配与回收— 首次适应法 This manuscript was revised by the office on December 10, 2020.

南通大学操作系统实验课 实验报告 学生姓名 所在院系 专业 学号 指导教师 南通大学 2014年 5 月 16 日主存空间的分配与回收 ——首次适应法 一、实验目的 主存是中央处理机能直接存取指令和数据的存储器,能否合理而有效地使用它,在很大程度上将影响整个计算机系统的性能。 本实验主要熟悉主存的管理方法以及相应的分配与回收算法。所谓分配,就是解决多道程序或多进程如何共享主存空间的问题,以便各个进程能获得所希望的主存空间,正确运行。所谓回收,就是当进程运行完成时,将其所占用的主存空间归还给系统。 二、实验要求 采用空闲区链法管理空闲区,并增加已分配区表。分配算法采用首次适应法。 三、设计思路: (1)采用空闲区链法管理空闲区,并增加已分配区表。分配算法采用首次适应法(内存空闲区的地址按照从小到大的自然顺序排列),实现内存的分配与回收。 (2)设计一个进程申请序列以及进程完成后的释放顺序,实现主存的分配与回收。

(3)进行分配时应该考虑这样3种情况:进程申请的空间小于、等于或大于系统空闲区的大小。回收时应该考虑这样4种情况:释放区上邻、下邻、上下都邻和都不邻接空闲区。 (4)每次的分配与回收都要求把记录内存使用情况的各种数据结构的变化情况以及各进程的申请、释放情况显示出来。 四、主要思想 (1)输入主存空间的最大长度n创建最大长度总和为n的若干空闲区的主存空闲区链; (2)输入待存作业的长度x,从链头开始找第一个合适作业的空闲区:分区长度小于x时,指针后移,继续寻找;分区长度等于x时,分配空间, 修改作业分区;分区长度大于x时,分配空间,修改分区数据。 五、流程图 1.空闲区链的首次适应算法分配流程图 2.空闲区链的首次适应算法回收流程图 六、调试结果 1.内存的分配 2.内存的回收 3.内存清空 七、总结与感悟 说实话我操作系统学得不是很好,一开始看到题目觉得自己要完成这个实验有些难度。好在老师提醒书上有另一道类似题目的程序代码,另外书上也有首次适应法的流程图,可以给我们一些提示。之后我也参考了网上的相关资料,看看别人是如何实现的,他们都是怎么样的思路和方法,与我一开始的想法相比,比我精妙在哪里。最后自己调试时,遇到了许许多多问题和错误,请教了学得比较好的同学、经过不断的修改和完善之后,终于做完实验。 这次的实验使我了解到,平时对知识的积累相当重要,同时也要注重课上老师的讲解,老师在课上的延伸是课本上所没有的,这些知识对于我们对程序的编写有很大的作用,同时,编程也要求我们有足够的耐心,细细推敲。越着急可能就越无法得到我们想要的结果,遇到不会的问题要多多请教,知识是在实践与向别人请教的过程中积累的,所以问是至关重要的,只要肯下功夫很多东西都是可以完成的。操作系统这门课不但重要而且十分有用,我一定要下功夫把这门课学好。

电脑内存不足及释放内存

第一招:关闭多余顺序 如果同时打开地文档过多或者运行地顺序过多,就没有足够地内存运行其他顺序.这时,对于多文档界面程序,如等,请关闭当前文档外地所有文档,并退出当前未使用地顺序,或许你就能够继续执行因“内存缺乏”而被中断地任务.资料个人收集整理,勿做商业用途 第二招:清除剪贴板中地内容 .清除系统剪贴板中地内容(存储复制或剪贴内容地剪贴板)点击“开始→顺序→附件→系统工具→剪贴板查看程序”编辑”菜单上,单击“删除”命令,系统弹出“清除剪贴板”对话框,单击“按钮.资料个人收集整理,勿做商业用途 .清除多重剪贴板中地内容(顺序提供地剪贴板)剪贴板”任务窗格(或工具栏(上,单击“全部清空”或“清空‘剪贴板’当清空“剪贴板”时,系统剪贴板也将同时被清空.资料个人收集整理,勿做商业用途 第三招:合理设置虚拟内存 如果没有设置虚拟内存,那么很容易收到内存缺乏”消息.点击“开始→设置→控制面板”双击“系统”系统属性”对话框中,单击“性能”选项卡,然后单击“虚拟内存”按钮.选中“让管理虚拟内存设置推荐)选项,将计算机中可作为虚拟内存使用地硬盘空间量设置为默认值.资料个人收集整理,勿做商业用途 第四招:增加可用磁盘空间 有四种方法可以增加磁盘地使用空间: .清空回收站. .删除临时文件.打开电脑”右键单击要释放其空间地磁盘,然后单击“属性”惯例”选项卡上,单击“磁盘清理”按钮,选中要删除地不需要地文件前地复选框进行整理.资料个人收集整理,勿做商业用途 .从磁盘中删除过期地文件或已存档地文件. .删除从未使用过地所有文件. 第五招:重新装置已损坏地顺序 如果仅仅是使用某个顺序时,系统提示内存缺乏,而其他顺序可以正常运行,那么可能地原因是该顺序文件被毁坏,从而导致内存缺乏地问题.请尝试删除偏重新安装该程序,然后重新运行该程序.如果系统不再提示内存缺乏,那么说明原顺序文件确实被损坏.资料个人收集整理,勿做商业用途 第六招:使用内存优化软件 内存优化软件有很多,比方和就比较出色.可以设置自动清空剪贴板、释放被关闭顺序未释放地内存,从而免除你手工操作地麻烦,达到自动释放内存地目地无妨一试.资料个人收集整理,勿做商业用途 第七招:重新启动计算机 如果只退出程序,并不重新启动计算机,顺序可能无法将内存资源归还给系统.运行重要顺序之前,请重新启动计算机以充分释放系统资源.资料个人收集整理,勿做商业用途 第八招:减少自动运行地顺序 如果在启动时自动运行地顺序太多,那么,即使重新启动计算机,也没足够地内存用来运行其他顺序.这时就需要清除一些不必要地系统自启动程序.点击“开始→运行”输入打开“系统配置实用顺序”窗口.单击“一般”选项卡,选中“选择性启动”复选框.去掉处置文件”和“加载启动项”前地复选框.打开“启动”选项卡,将不需要开机自动启动地顺序都勾除掉就好了资料个人收集整理,勿做商业用途 第九招:查杀病毒 系统感染电脑病毒也是导致内存缺乏地罪魁祸首.当系统出现“内存缺乏”错误时,请使用最

C++容器

C++容器类 分类:C++2014-07-13 09:56 34人阅读评论(0) 收藏举报什么是容器 首先,我们必须理解一下什么是容器,在C++ 中容器被定义为:在数据存储上,有一种对象类型,它可以持有其它对象或指向其它对像的指针,这种对象类型就叫做容器。很简单,容器就是保存其它对象的对象,当然这是一个朴素的理解,这种“对象”还包含了一系列处理“其它对象”的方法,因为这些方法在程序的设计上会经常被用到,所以容器也体现了一个好处,就是“容器类是一种对特定代码重用问题的良好的解决方案”。 容器还有另一个特点是容器可以自行扩展。在解决问题时我们常常不知道我们需要存储多少个对象,也就是说我们不知道应该创建多大的内存空间来保存我们的对象。显然,数组在这一方面也力不从心。容器的优势就在这里,它不需要你预先告诉它你要存储多少对象,只要你创建一个容器对象,并合理的调用它所提供的方法,所有的处理细节将由容器来自身完成。它可以为你申请内存或释放内存,并且用最优的算法来执行您的命令。 容器是随着面向对象语言的诞生而提出的,容器类在面向对象语言中特别重要,甚至它被认为是早期面向对象语言的基础。在现在几乎所有的

面向对象的语言中也都伴随着一个容器集,在C++ 中,就是标准模板库(STL )。 和其它语言不一样,C++ 中处理容器是采用基于模板的方式。标准C++ 库中的容器提供了多种数据结构,这些数据结构可以与标准算法一起很好的工作,这为我们的软件开发提供了良好的支持! 通用容器的分类 STL 对定义的通用容器分三类:顺序性容器、关联式容器和容器适配器。 顺序性容器是一种各元素之间有顺序关系的线性表,是一种线性结构的可序群集。顺序性容器中的每个元素均有固定的位置,除非用删除或插入的操作改变这个位置。这个位置和元素本身无关,而和操作的时间和地点有关,顺序性容器不会根据元素的特点排序而是直接保存了元素操作时的逻辑顺序。比如我们一次性对一个顺序性容器追加三个元素,这三个元素在容器中的相对位置和追加时的逻辑次序是一致的。 关联式容器和顺序性容器不一样,关联式容器是非线性的树结构,更准确的说是二叉树结构。各元素之间没有严格的物理上的顺序关系,也就是说元素在容器中并没有保存元素置入容器时的逻辑顺序。但是关联式容器提供了另一种根据元素特点排序的功能,这样迭代器就能根据元素的特点“顺序地”获取元素。

内存分配与回收

课程设计 题目:主存空间的分配与回收 学生姓名: 学院:信息工程学院 系别:计算机系 专业:计算机科学与技术 班级:计算机 指导教师:副教授 副教授 2011年月日 内蒙古工业大学课程设计任务书(三) 学院(系):信息学院计算机系课程名称:操作系统课程设计指导教师(签名):专业班级:计算机09-2 学生姓名:学号:

目录 第一章背景研究 (1) 1.1课题简介 (1) 1.2 设计要求 (1) 1.3概念原理 (1) 1.4 环境说明和使用工具 (2) 第二章详细设计 (2) 2.1功能介绍 (2) 2.1.1分配函数发fenpei()的执行过程(最佳适应算法) (2) 2.1.2回收进程空间所占的函数free()的执行过程 (2) 2.2函数的规格说明 (3) 2.2.1打印分配表空闲表函数 print() (3) 2.2.2为进程分配空间函数 fenpei(char *c, struct node *p,struct node *f) (3) 2.2.3回收进程所占空间函数struct node * free(char *c, struct node *p,struct node *f) (3) 2.3 主要数据结构 (3) 2.4 流程图 (5) 第三章核心算法的实现 (6) 3.1 分配函数 (6) 3.2回收函数 (11) 第四章测试 (15) 4.1 预测试 (15) 4.2 实际运行结果(截图) (16) 第五章总结 (18) 参考文献 (25)

第一章背景研究 1.1课题简介 操作系统是当代计算机软件系统的核心,是计算机系统的基础和支撑,它管理和控制着计算机系统中的所有软、硬件资源,可以说操作系统是计算机系统的灵魂。操作系统课程是计算机专业学生必须学习和掌握的基础课程, 是计算机应用人员深入了解和使用计算机的必备知识, 是进行系统软件开发的理论基础,也是计算机科学与技术专业的一门理论性和实践性并重的核心主干课程。本课程的目的是使学生掌握现代计算机操作系统的基本原理、基本设计方法及实现技术,具有分析现行操作系统和设计、开发实际操作系统的基本能力。 通过本次课程设计熟悉主存空间的分配与回收,所谓分配,就是解决多道作业或多进程如何共享主存空间的问题。所谓回收,就是当作业运行完成时,将作业或进程所占用的主存空间归还给系统。采用可变式分区管理,使用最佳适应算法实现主存的分配与回收。深入研究此算法有助于我们全面的理解内存的分配原理,培养我们逻辑思维能力。 1.2 设计要求 设计多个作业或进程动态请求内存资源的模拟系统,使用最佳适应算法实现内存的分配与回收,实现可变式分区管理;设计相应的内存分配算法,定义相关数据结构,以及输出显示每次请求分配内存的结果和内存的已分配和未分配的状况。 1.3概念原理 可变式分区管理的原理:区域的大小及起始地址是可变的,根据程序装入时的大小动态地分配一个区域。保证每个区域之中刚好放一个程序。这样可以充分地利用存储空间,提高内存的使用效率。如果一个程序运行完毕,就要释放出它所占有的分区,使之变成空闲区。这样就会出现空闲区与占用区相互交错的情况。这样就需要P 表,F表来分别表示内存的占用区状态与空闲区的状态。

C语言中多维数组的内存分配和释放

写代码的时候会碰到多维数组的内存分配和释放问题,在分配和释放过程中很容易出现错误。下面贴上一些示例代码,以供参考。 如果要给二维数组(m*n)分配空间,代码可以写成下面: char **a, i; // 先分配m个指针单元,注意是指针单元 // 所以每个单元的大小是sizeof(char *) a = (char **)malloc(m * sizeof(char *)); // 再分配n个字符单元, // 上面的m个指针单元指向这n个字符单元首地址 for(i = 0; i < m; i++) a[i] = (char *)malloc(n * sizeof(char)); (注意红色部分) 释放应该是: int i; for(i=0;i

a = (char ***)malloc(m * sizeof(char **)); for(i = 0; i < m; ++i) a[i] = (char **)malloc(n * sizeof(char *)); for(i = 0; i < m; ++i) for(j = 0; j < n; ++j) a[i][j] = (char *)malloc(p * sizeof(char)); 释放代码为逆过程,具体代码为: int i,j,; for(i = 0; i < m; ++i) for(j = 0; j < n; ++j) free((void *)a[i][j]); for(i = 0; i < m; ++i) free((void *)a[i]); free((void *)a); 三维以上的多维数组的分配和释放,原理与上面的一样。 (转) C中如何为第二维长度固定的二维数组分配内存

vector的应用

C++内置的数组支持容器的机制,但是它不支持容器抽象的语义。要解决此问题我们自己实现这样的类。在标准C++中,用容器向量(vector)实现。容器向量也是一个类模板。 标准库vector类型使用需要的头文件:#include 。vector 是一个类模板。不是一种数据类型,vector是一种数据类型。Vector的存储空间是连续的,list不是连续存储的。 一、定义和初始化 vector< typeName > v1; //默认v1为空,故下面的赋值是错误的 v1[0]=5; vectorv2(v1); 或v2=v1;或vector v2(v1.begin(), v1.end());//v2是v1的一个副本,若v1.size()>v2.size()则赋值后v2.size()被扩充为v1.size()。 vector< typeName > v3(n,i);//v3包含n个值为i的typeName类型元素vector< typeName > v4(n); //v4含有n个值为0的元素 int a[4]={0,1,2,3,3}; vector v5(a,a+5);//v5的size为5,v5被初始化为a 的5个值。后一个指针要指向将被拷贝的末元素的下一位置。 vector v6(v5);//v6是v5的拷贝 vector< 类型 > 标识符(最大容量,初始所有值); 二、值初始化 1> 如果没有指定元素初始化式,标准库自行提供一个初始化值进行值初始化。 2> 如果保存的式含有构造函数的类类型的元素,标准库使用该类型的构造函数初始化。 3> 如果保存的式没有构造函数的类类型的元素,标准库产生一个带初始值的对象,使用这个对象进行值初始化。 三、vector对象最重要的几种操作 1. v.push_back(t)在容器的最后添加一个值为t的数据,容器的size变大。 另外list有push_front()函数,在前端插入,后面的元素下标依次增大。 2. v.size() 返回容器中数据的个数,size返回相应vector类定义的size_type的值。v.resize(2*v.size)或

Java 内存释放

Java 内存释放 (问题一:什么叫垃圾回收机制?)垃圾回收是一种动态存储管理技术,它自动地释放不再被程序引用的对象,按照特定的垃圾收集算法来实现资源自动回收的功能。当一个对象不再被引用的时候,内存回收它占领的空间,以便空间被后来的新对象使用,以免造成内存泄露。 (问题二:java的垃圾回收有什么特点?)JAVA语言不允许程序员直接控制内存空间的使用。内存空间的分配和回收都是由JRE负责在后台自动进行的,尤其是无用内存空间的回收操作(garbagecollection,也称垃圾回收),只能由运行环境提供的一个超级线程进行监测和控制。 (问题三:垃圾回收器什么时候会运行?)一般是在CPU空闲或空间不足时 自动进行垃圾回收,而程序员无法精确控制垃圾回收的时机和顺序等。 (问题四:什么样的对象符合垃圾回收条件?)当没有任何获得线程能访问一个对象时,该对象就符合垃圾回收条件。 (问题五:垃圾回收器是怎样工作的?)垃圾回收器如发现一个对象不能被任何活线程访问时,他将认为该对象符合删除条件,就将其加入回收队列,但不是立即销毁对象,何时销毁并释放内存是无法预知的。垃圾回收不能强制执行,然 而Java提供了一些方法(如:System.gc()方法),允许你请求JVM执行垃圾回收,而不是要求,虚拟机会尽其所能满足请求,但是不能保证JVM从内存中删除所有不用的对象。 (问题六:一个java程序能够耗尽内存吗?)可以。垃圾收集系统尝试在对 象不被使用时把他们从内存中删除。然而,如果保持太多活的对象,系统则可能会耗尽内存。垃圾回收器不能保证有足够的内存,只能保证可用内存尽可能的得到高效的管理。 (问题七:如何显示的使对象符合垃圾回收条件?) (1)空引用:当对象没有对他可到达引用时,他就符合垃圾回收的条件。也就是说如果没有对他的引用,删除对象的引用就可以达到目的,因此我们可以把引用变量设置为null,来符合垃圾回收的条件。 Java代码 1.StringBuffer sb = new StringBuffer("hello");

北理工操作系统内存管理实验报告

实验三:内存管理 班级: 学号:

姓名: 一、实验目的 1.通过编写和调试存储管理的模拟程序以加深对存储管理方案的理解; 2.熟悉虚存管理的页面淘汰算法; 3.通过编写和调试地址转换过程的模拟程序以加强对地址转换过程的了解。 二、实验要求 1.设计一个请求页式存储管理方案(自己指定页面大小),并予以程序实现。 并产生一个需要访问的指令地址流。它是一系列需要访问的指令的地址。为不失一般性,你可以适当地(用人工指定地方法或用随机数产生器)生成这个序列。 2.页面淘汰算法采用FIFO页面淘汰算法,并且在淘汰一页时,只将该页在页 表中抹去。而不再判断它是否被改写过,也不将它写回到辅存。 3.系统运行既可以在Windows,也可以在Linux。 三、实验流程图

图1 页式存储管理程序参考流程 四、实验环境 硬件设备:个人计算机。 系统软件:windows操作系统,Visual C++6.0编译环境。 五、实验结果

说明:模拟产生35个指令地址,随机产生20个指令地址进行排队,假设主存中共有10个工作集页帧。将前9个指令调入内存,因为前9个指令中,页号为13的指令有两个,所以调入内存中共有8页。此时主存中还有两个空闲帧。此时按刚才随机顺序进行访问指令工作。前9页因都在主存中可直接调用。第10个随机地址为页号为5的指令,也在主存中,也可直接调用。页号为24,3因不在主存中,需要调用进主存。此时主存已满。然后主存需要进行调用页号为27号的指令,因主存已满,需要执行FIFO算法,将最先进入主存的页号为30的指令调出,将27号放入第1000000帧。以后需要调用的页面按照存在就无需调用,否则按FIFO原则进行调页工作。 六、实验感想 七、实验代码 #include

操作系统之内存分配与回收

操作系统实验 内存的分配与回收 实验报告 一、实验题目:内存的分配与回收 二、实验内容:利用可变分区的首次适应算法,模拟内存的分配与回收。 三、实验目的:掌握可变分区首次适应算法的原理以及其编程实现。 四、实验过程: 1、基本思想:可变分区分配是根据进程的实际需求,动态地为之分配内存空间。首次适应算法要求空闲空间链以地址递增的次序链接。进行内存分配时,从链表头部开始依次检索,找到第一个不小于请求空间大小的空闲空间进行分配。分配时需考虑碎片问题,若分配会导致碎片产生则将整块分区分配。内存的回收需要考虑四种情况:⑴回收分区前后两个分区都空闲,则需要和前后两个分区合并;(2)回收分区只有前一分区空闲,则与前一分区合并;(3)回收分区只有后一分区空闲,则和后一分区合并;(4)回收分区独立,不考虑合并 。 2、主要数据结构: struct FreeArea{ 链结点包含的数据:分区号、大小、起址、标记 i nt ID; i nt size;

l ong address; i nt sign; }; struct Node { 双链表结点结构体:数据区、前向指针、后继指针 F reeArea data; s truct Node *prior; s truct Node *next; }*DLinkList; 3、输入、输出: 输入: I.内存分配时由键盘输入分区ID和大小; II.内存回收时由键盘输入需要回收的分区ID; 输出:输出内存的分配情况(按照地址从低到高) 4、程序流程图:

5、实验结果截屏:

6、源程序代码: #include using namespace std; #define Free 0 //空闲状态 #define Busy 1 //已用状态 #define PBusy 2 //碎片已用状态 #define FINISH 1 //完成 #define FINISH2 1 //完成 #define ERROR 0 //出错 #define memory 512 //最大内存空间为(单位:KB)#define min 10 //碎片最小值(单位:KB) typedef struct FreeArea//空闲链数据 { i nt ID; i nt size; l ong address; i nt sign; }; typedef struct Node//空闲连结构 { F reeArea data;

频繁分配释放内存导致的性能问题分析

内核态与用户态是操作系统的两种运行级别,intel cpu提供Ring0-Ring3三种级别的运行模式。Ring0级别最高,Ring3最低。 当一个任务(进程)执行系统调用而陷入内核代码中执行时,我们就称进程处于内核运行态(或简称为内核态)。此时处理器处于特权级最高的(0级) 内核代码中执行。当进程处于内核态时,执行的内核代码会使用当前进程的内核栈。每个进程都有自己的内核栈。当进程在执行用户自己的代码时,则称其处于用户运行态(用户态)。即此时处理器在特权级最低的(3级)用户代码中运行。 在内核态下CPU可执行任何指令,在用户态下CPU只能执行非特权指令。当CPU处于内核态,可以随意进入用户态;而当CPU处于用户态时,用户从用户态切换到内核态只有在系统调用和中断两种情况下发生,一般程序一开始都是运行于用户态,当程序需要使用系统资源时,就必须通过调用软中断进入内核态。 现象 1 压力测试过程中,发现被测对象性能不够理想,具体表现为: 进程的系统态CPU消耗20,用户态CPU消耗10,系统idle大约70 2 用ps -o majflt,minflt -C program命令查看,发现majflt每秒增量为0,而minflt每秒增量大于10000。 初步分析 majflt代表major fault,中文名叫大错误,minflt代表minor fault,中文名叫小错误。 这两个数值表示一个进程自启动以来所发生的缺页中断的次数。 当一个进程发生缺页中断的时候,进程会陷入内核态,执行以下操作: 检查要访问的虚拟地址是否合法 查找/分配一个物理页 填充物理页内容(读取磁盘,或者直接置0,或者啥也不干) 建立映射关系(虚拟地址到物理地址) 重新执行发生缺页中断的那条指令 如果第3步,需要读取磁盘,那么这次缺页中断就是majflt,否则就是minflt。 此进程minflt如此之高,一秒10000多次,不得不怀疑它跟进程内核态cpu消耗大有很大关系。 分析代码 查看代码,发现是这么写的:一个请求来,用malloc分配2M内存,请求结束后free这块内存。看日志,发现分配内存语句耗时10us,平均一条请求处理耗时1000us 。原因已找到! 虽然分配内存语句的耗时在一条处理请求中耗时比重不大,但是这条语句严重影响了性能。要解释清楚原因,需要先了解一下内存分配的原理。 内存分配的原理 从操作系统角度来看,进程分配内存有两种方式,分别由两个系统调用完成:brk和mmap (不考虑共享内存)。brk是将数据段(.data)的最高地址指针_edata往高地址推,mmap是在进程的虚拟地址空间中(一般是堆和栈中间)找一块空闲的。这两种方式分配的都是虚拟内存,没有分配物理内存。在第一次访问已分配的虚拟地址空间的时候,发生缺页中断,操作系统负责分配物理内存,然后建立虚拟内存和物理内存之间的映射关系。

java垃圾回收机制

上次讲到引用类型和基本类型由于内存分配上的差异导致的性能问题。那么今天就来聊一下和内存释放(主要是gc)有关的话题。 事先声明一下:虽说sun公司已经被oracle吞并了,但是出于习惯,同时也为了偷懒节省打字,以下仍然称之为sun公司。 ★jvm的内存 在java虚拟机规范中(具体章节请看“这里”),提及了如下几种类型的内存空间: ◇栈内存(stack):每个线程私有的。 ◇堆内存(heap):所有线程公用的。 ◇方法区(method area):有点像以前常说的“进程代码段”,这里面存放了每个加载类的反射信息、类函数的代码、编译时常量等信息。 ◇原生方法栈(native method stack):主要用于jni中的原生代码,平时很少涉及。 关于栈内存(stack)和堆内存(heap),已经在上次的帖子中扫盲过了,大伙儿应该有点印象。由于今天咱们要讨论的“垃圾回收”话题,主要是和堆内存(heap)有关。其它的几个玩意儿不是今天讨论的重点。等以后有空了,或许可以单独聊一下。 ★垃圾回收机制简介 其实java虚拟机规范中并未规定垃圾回收的相关细节。垃圾回收具体该怎么搞,完全取决于各个jvm的设计者。所以,不同的jvm之间,gc的行为可能会有一定的差异。下面咱拿sun官方的jvm来简单介绍一下gc的机制。 ◇啥时候进行垃圾回收? 一般情况下,当jvm发现堆内存比较紧张、不太够用时,它就会着手进行垃圾回收工作。但是大伙儿要认清这样一个残酷的事实:jvm进行gc的时间点是无法准确预知的。因为gc启动的时刻会受到各种运行环境因素的影响,随机性太大。 虽说咱们无法准确预知,但如果你想知道每次垃圾回收执行的情况,还是蛮方便的。可以通过jvm的命令行参数“-xx:+printgc”把相关信息打印出来。 另外,调用system.gc()只是建议jvm进行gc。至于jvm到底会不会做,那就不好说啦。通常不建议自己手动调用system.gc(),还是让jvm自行决定比较好。另外,使用jvm命令行参数“-xx:+disableexplicitgc”可以让system.gc()不起作用。 ◇谁来负责垃圾回收? 一般情况下,jvm会有一个或多个专门的垃圾回收线程,由它们负责清理回收垃圾内存。 ◇如何发现垃圾对象? 垃圾回收线程会从“根集(root set)”开始进行对象引用的遍历。所谓的“根集”,就是正在运行的线程中,可以访问的引用变量的集合(比如所有线程当前函数的参数和局部变量、当前类的成员变量等等)。垃圾回收线程先找出被根集直接引用的所有对象(不妨叫集合1),然后再找出被集合1直接引用的所有对象(不妨叫集合2),然后再找出被集合2直接引用的所有对象......如此循环往复,直到把能遍历到的对象都遍历完。 凡是从根集通过上述遍历可以到达的对象,都称为可达对象或有效对象;反之,则是不可达对象或失效对象(也就是垃圾)。 ◇如何清理/回收垃圾? 通过上述阶段,就把垃圾对象都找出来。然后垃圾回收线程会进行相应的清理和回收工作,包括:把垃圾内存重新变为可用内存、进行内存的整理以消除内存碎片、等等。这个过程会涉及到若干算法,有兴趣的同学可以参见“这里”。限于篇幅,咱就不深入聊了。 ◇分代 早期的jvm是不采用分代技术的,所有被gc管理的对象都存放在同一个堆里面。这么做的缺点比较明显:每次进行gc都要遍历所有对象,开销很大。其实大部分的对象生命周期都很短(短命对象),只有少数对象比较长寿;在这些短命对象中,又只有少数对象占用的内存空间大;其它大量的短命对象都属于小对象(很符合二八原理)。 有鉴于此,从jdk 1.2之后,jvm开始使用分代的垃圾回收(generational garbage collection)。jvm把gc相关的内存分为年老代(tenured)和年轻代(nursery)、持久代(permanent,对应于jvm规范的方法区)。大部分对象在刚创建时,都位于年轻代。如果某对象经历了几轮gc还活着(大龄对象),就把它移到年老代。另外,如果某个对象在创建时比较大,可能就直接被丢到年老代。经过这种策略,使得年轻代总是保存那些短命的小对象。在空间尺寸上,年轻代相对较小,而年老代相对较大。 因为有了分代技术,jvm的gc也相应分为两种:主要收集(major collection)和次要收集(minor collection)。主要收集同时清理年老代和年轻代,因此开销很大,不常进行;次要收集仅仅清理年轻代,开销很小,经常进行。 ★gc对性能会有啥影响? 刚才介绍了gc的大致原理,那gc对性能会造成哪些影响捏?主要有如下几个方面: ◇造成当前运行线程的停顿 早期的gc比较弱智。在它工作期间,所有其它的线程都被暂停(以免影响垃圾回收工作)。等到gc干完活,其它线程再继续运行。所以,早期jdk的gc一旦开始工作,整个程序就会陷入假死状态,失去各种响应。

c++中vector的用法详解

c++中vector的用法详解 vector(向量): C++中的一种数据结构,确切的讲是一个类.它相当于一个动态的数组,当程序员无法明白自己需要的数组的规模多大时,用其来解决咨询题能够达到最大节约空间的目的. 用法: 1.文件包含: 首先在程序开头处加上#include以包含所需要的类文件vector 还有一定要加上using namespace std; 2.变量声明: 2.1 例:声明一个int向量以替代一维的数组:vector a;(等于声明了一个int数组a[],大小没有指定,能够动态的向里面添加删除)。 2.2 例:用vector代替二维数组.事实上只要声明一个一维数组向量即可,而一个数组的名字事实上代表的是它的首地址,因此只要声明一个地址的向量即可,即:vector a.同理想用向量代替三维数组也是一样,vector a;再往上面依此类推. 3.具体的用法以及函数调用: 3.1 如何得到向量中的元素?其用法和数组一样: 例如: vector a int b = 5; a.push_back(b);//该函数下面有详解 cout c. c.clear()移除容器中所有数据。 c.empty()推断容器是否为空。 c.erase(pos)删除pos位置的数据 c.erase(beg,end)删除[beg,end)区间的数据 c.front()传回第一个数据。 c.insert(pos,elem)在pos位置插入一个elem拷贝 c.pop_back()删除最后一个数据。 c.push_back(elem)在尾部加入一个数据。 c.resize(num)重新设置该容器的大小 c.size()回容器中实际数据的个数。 c.begin()返回指向容器第一个元素的迭代器 c.end()返回指向容器最后一个元素的迭代器 4.内存治理与效率 1》使用reserve()函数提早设定容量大小,幸免多次容量扩充操作导致效率低下。 关于STL容器,最令人赞扬的特性之一就是是只要不超过它们的最大大小,它们就能够自动增长到足以容纳你放到里面去的数据。(要明白那个最大值,只要调用名叫max_size的成员函数。)关于vector和string,假如需要更多空间,就以类似realloc的思想来增长大小。vector 容器支持随机访问,因此为了提高效率,它内部使用动态数组的方式实现的。在通过reserve() 来申请特定大小的时候总是按指数边界来增大其内部缓冲区。当进行insert或push_back等增加元素的操作时,假如如今动态数组的内存不够用,就要动态的重新分配当前大小的1.5~2倍的新内存区,再把原数组的内容复制过去。因此,在一般情况下,其访问速度同一般数组,只有在重新分配发生时,其性能才会下落。正如上面的代码告诉你的那样。而进行pop_back

内存的申请与释放

实习四 主存储器空间的分配和回收 一、实习内容 主存储器空间的分配和回收。 二、实习目的 一个好的计算机系统不仅要有一个足够容量的、存取速度高的、稳定可靠的主存储器,而且要能合理地分配和使用这些存储空间。当用户提出申请存储器空间时,存储管理必须根据申请者的要求,按一定的策略分析主存空间的使用情况,找出足够的空闲区域分配给申请者。当作业撤离或主动归还主存资源时,则存储管理要收回作业占用的主存空间或归还部分主存空间。主存的分配和回收的实现虽与主存储器的管理方式有关的,通过本实习帮助学生理解在不同的存储管理方式下应怎样实现主存空间的分配和回收。 三、实习题目 本实习模拟在两种存储管理方式下的主存分配和回收。 第一题:在可变分区管理方式下采用最先适应算法实现主存分配和实现主存回收。 [提示]: 可变分区方式是按作业需要的主存空间大小来分割分区的。当要装入一个作业时,根据作业需要的主存量查看是否有足够的空闲空间,若有,则按需要量分割一个分区分配给该作业;若无,则作业不能装入。随着作业的装入、撤离,主存空间被分成许多个分区,有的分区被作业占用,而有的分区是空闲的。例如: 为了 说明哪些区是空闲的,可以用来装入新作业,必须要有一张空闲区说明表,格式如下: 第一栏 第二栏 其中,起址——指出一个空闲区的主存起始地址。 长度——指出从起始地址开始的一个连续空闲的长度。 状态——有两种状态,一种是“未分配”状态,指出对应的由起址指出的某个长度的区域是空闲区;另一种是“空表目”状态,表示表中对应的登记项目是空白(无效),可用

来登记新的空闲区(例如,作业撤离后,它所占的区域就成了空闲区,应找一个“空表目”栏登记归还区的起址和长度且修改状态)。由于分区的个数不定,所以空闲区说明表中应有适量的状态为“空表目”的登记栏目,否则造成表格“溢出”无法登记。 上述的这张说明表的登记情况是按提示(1)中的例所装入的三个作业占用的主存区域后填写的。 (2) 当有一个新作业要求装入主存时,必须查空闲区说明表,从中找出一个足够大的空闲区。有时找到的空闲区可能大于作业需要量,这时应把原来的空闲区变成两部分:一部分分给作业占用;另一部分又成为一个较小的空闲区。为了尽量减少由于分割造成的空闲区,而尽量保存高地址部分有较大的连续空闲区域,以利于大型作业的装入。为此,在空闲区说明表中,把每个空闲区按其地址顺序登记,即每个后继的空闲区其起始地址总是比前者大。为了方便查找还可使表格“紧缩”,总是让“空表目”栏集中在表格的后部。 (3) 采用最先适应算法(顺序分配算法)分配主存空间。 按照作业的需要量,查空闲区说明表,顺序查看登记栏,找到第一个能满足要求的空闲区。当空闲区大于需要量时,一部分用来装入作业,另一部分仍为空闲区登记在空闲区说明表中。 由于本实习是模拟主存的分配,所以把主存区分配给作业后并不实际启动装入程序装入作业,而用输出“分配情况”来代替。最先适应分配算法如图4-1。 (4) 当一个作业执行结束撤离时,作业所占的区域应该归还,归还的区域如果与其它空闲区相邻,则应合成一个较大的空闲区,登记在空闲区说明表中。例如,在提示(1)中列举的情况下,如果作业2撤离,归还所占主存区域时,应与上、下相邻的空闲区一起合成一个大的空闲区登记在空闲区说明表中。归还主存时的回收算法如图4-2。 (5) 请按最先适应算法设计主存分配和回收的程序。然后按(1)中假设主存中已装入三个作业,且形成两个空闲区,确定空闲区说明表的初值。现有一个需要主存量为6K的作业4申请装入主存;然后作业3撤离;再作业2撤离。请你为它们进行主存分配和回收,把空闲区说明表的初值以及每次分配或回收后的变化显示出来或打印出来。 第二题:在分页式管理方式下采用位示图来表示主存分配情况,实现主存空间的分配和回收。 [提示]: (1) 分页式存储器把主存分成大小相等的若干块,作业的信息也按块的大小分页,作业装入主存时可把作业的信息按页分散存放在主存的空闲块中,为了说明主存中哪些块已经被占用,哪些块是尚未分配的空闲块,可用一张位示图来指出。位示图可由若干存储单元来构成,其中每一位与一个物理块对应,用0/1表示对应块为空闲/已占用。 (2) 假设某系统的主存被分成大小相等的64块,则位示图可用8个字节来构成,另用一单元记录当前空闲块数。如果已有第0,1,4,5,6,9,11,13,24,31,共10个主存

JVM内存分配(栈堆)与JVM回收机制

Java 中的堆和栈 简单的说: Java把内存划分成两种:一种是栈内存,一种是堆内存。 在函数中定义的一些基本类型的变量和对象的引用变量都在函数的栈内存中分配。 当在一段代码块定义一个变量时,Java就在栈中为这个变量分配内存空间,当超过变量的作用域后,Java会自动释放掉为该变量所分配的内存空间,该内存空间可以立即被另作他用。 堆内存用来存放由new创建的对象和数组。 在堆中分配的内存,由Java虚拟机的自动垃圾回收器来管理。 在堆中产生了一个数组或对象后,还可以在栈中定义一个特殊的变量,让栈中这个变量的取值等于数组或对象在堆内存中的首地址,栈中的这个变量就成了数组或对象的引用变量。 引用变量就相当于是为数组或对象起的一个名称,以后就可以在程序中使用栈中的引用变量来访问堆中的数组或对象。 具体的说: 栈与堆都是Java用来在Ram中存放数据的地方。与C++不同,Java自动管理栈和堆,程序员不能直接地设置栈或堆。 Java的堆是一个运行时数据区,类的(对象从中分配空间。这些对象通过new、newarray、anewarray和multianewarray等指令建立,它们不需要程序代码来显式的释放。堆是由垃圾回收来负责的,堆的优势是可以动态地分配内存大小,生存期也不必事先告诉编译器,因为它是在运行时动态分配内存的,Java的垃圾收集器会自动收走这些不再使用的数据。但缺点是,由于要在运行时动态分配内存,存取速度较慢。 栈的优势是,存取速度比堆要快,仅次于寄存器,栈数据可以共享。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。栈中主要存放一些基本类型的变量(,int, short, long, byte, float, double, boolean, char)和对象句柄。 栈有一个很重要的特殊性,就是存在栈中的数据可以共享。假设我们同时定义: int a = 3; int b = 3; 编译器先处理int a = 3;首先它会在栈中创建一个变量为a的引用,然后查找栈中是否有3这个值,如果没找到,就将3存放进来,然后将a指向3。接着处理int b = 3;在创建完b的引用变量后,因为在栈中已经有3这个值,便将b直接指向3。这样,就出现了a与b同时均指向3的情况。这时,如果再令a=4;那么编译器会重新搜索栈中是否有4值,如果没有,则将4存放进来,并令a指向4;如果已经有了,则直接将a指向这个地址。因此a值的改变不会影响到b 的值。要注意这种数据的共享与两个对象的引用同时指向一个对象的这种共享是不同的,因为这种情况a的修改并不会影响到b, 它是由编译器完成的,它有利于节省空间。而一个对象引用变量修改了这个对象的内部状态,会影响到另一个对象引用变量。 String是一个特殊的包装类数据。可以用: String str = new String("abc"); String str = "abc"; 两种的形式来创建,第一种是用new()来新建对象的,它会在存放于堆中。每调用一次就会创建一个新的对象。 而第二种是先在栈中创建一个对String类的对象引用变量str,然后查找栈中有没有存放"abc",如果没有,则将"abc"存放进栈,并令str指向”abc”,如果已经有”abc”则直接令 str指向“abc”。 比较类里面的数值是否相等时,用equals()方法;当测试两个包装类的引用是否指向同一个对象时,用==,下面用例子说明上面的理论。 String str1 = "abc"; String str2 = "abc"; System.out.println(str1==str2); //true

真正彻底释放、手机内存可用空间

手机需 .打开管理器,进入手机目录下,里面全是一些数据文件,不管软件安装在手机或内存卡,都会在这里生成文件,特别是当软件删除后,文件仍然留在此目录下.资料个人收集整理,勿做商业用途 .文件名全部为英文,大家仔细看文件名后看软件是否已经删除,删除了地就可以直接删除文件,每个文件占用空间都比较大,真正彻底释放手机内存可用空间.资料个人收集整理,勿做商业用途 .当然后删除自带地软件里面也是有残留文件地,对准软件名后可以一一删除,如果哪个文件名不知具体是哪个软件,多百度吧.资料个人收集整理,勿做商业用途 .打开管理器,进入手机目录下,里面是一些日志文件,占用地空间也是非常大地,可以全部删除,不过开机后仍然有两三个文件会自动生成,没关系.资料个人收集整理,勿做商业用途 深度清理三星安卓手机各种残留文件,释放手机内存可用空间”教程 、本教程由官方出品,适用所有三星安卓手机:~4.1.2,自己用了久,效果刚刚滴!论坛没人发,我给大家分享一下!资料个人收集整理,勿做商业用途 、深度清理三星安卓手机各种残留文件教程: 、首先,你得要,对于有系统洁癖滴你来说,眼里揉不进沙子呵呵,那是必须装文件浏览器地. 、进入浏览器,然后第二个文件就是了,进入后删除全部即可. 、返回浏览器主页,找到文件夹,进入后往下拉,找到文件夹,进入后删除全部即可,有时候你地手机会因为这样那样滴原因而资料个人收集整理,勿做商业用途 、产生系统错误,就会产生那个高达左右滴文件了. 、接下来,进入刺激滴环节,在浏览器主页面,找到文件夹,往下拉,找到文件夹,进入后你会看到诸多结尾滴文资料个人收集整理,勿做商业用途 、件,有些你通过名字即可判知其所属软件程序.那为啥要清理那些文件呢?因为在你安装软件、游戏后,就好在这个目录下产生文件,资料个人收集整理,勿做商业用途 、而删除软件后,它还存在地而且你可以借助文件浏览器看一下他们滴大小,呵呵,很惊人吧. 、那接下来就是点击最左下角滴虚拟功能键,点击“多选模式”,然后点“全部选择”,点击“删除”即可,接下来要赶紧做地事情就是资料个人收集整理,勿做商业用途 、退出浏览器,迅速关机,否则时间长了就会产生系统错误通知. 、开机后再去看看这些文件,是地,他们又自动生成了! 但是删除软件地那些文件就消失地无影无踪了,这样可以有效清理很多无资料个人收集整理,勿做商业用途 、用地废品,节省空间. 全面清理三星安卓手机各种残留文件教程 首先,你得要,对于有系统洁癖滴你来说,眼里揉不进沙子呵呵,那是必须装文件管理器地. 进入管理器,然后第二个文件就是了,进入后删除全部即可. 返回管理器主页,找到文件夹,进入后往下拉,找到文件夹,进入后删除全部即可,有时候你地手机会因为这样那样滴原因而资料个人收集整理,勿做商业用途 产生系统错误,就会产生那个高达左右滴文件了. 接下来,进入刺激滴环节,在管理器主页面,找到文件夹,往下拉,找到文件夹,进入后你会看到诸多结尾滴文资料个人收集整理,勿做商业用途 件,有些你通过名字即可判知其所属软件程序.那为啥要清理那些文件呢?因为在你安装软件、游戏后,就好在这个目录下产生文件,资料个人收集整理,勿做商业用途

相关文档