# 基于JAVA的内存管理模拟
# 一、需求分析
为了更好地理解操作系统内存分配和管理的过程和机制,决定通过编程模拟操作系统内存分配的过程,以更好的理解操作系统内存分配过程中的具体执行流程。
题目描述如下:
编写一个程序,包括两个线程,一个线程用于模拟内存分配活动,另一个用于跟踪第一个线程的内存行为,要求两个线程之间通过信号量实现同步,模拟内存活动的线程可以从一个文件中读出要进行的内存操作。每个内存操作包含如下内容:
- 时间:每个操作等待时间
- 块数:分配内存的粒度
- 操作:包括保留一个区域、提交一个区域、释放一个区域、回收一个区域、加锁与解锁一个区域。可将它们的编号放置于一个文件中
- 保留是指保留进程的虚地址空间,而不分配物理地址空间
- 提交是指在内存中分配物理地址空间
- 回收是指释放物理地址空间,而保留进程的虚地址空间
- 释放是指将进程的物理地址与虚拟地址空间全部释放
- 大小:块的大小
- 访问权限:共五种 PAGE\_READONLY, PAGE\_READWRIYE, PAGE\_EXEXUTE, PAGE\_EXEXUTE\_READ, PAGE\_EXEXUTE\_READWRIYE
# 二、概要设计
根据题意,要实现两个线程,通过内存分配线程从操作文件中读取操作参数,实现模拟内存分配的过程,监听线程则监视内存分配线程的执行过程,对内存分配过程中的模拟内存详细信息进行输出。采用Java的Thread实现对线程的创建,并通过重写run()方法,来设定线程中的执行代码。
**内存分配线程流程图**
![](http://www.writebug.com/myres/static/uploads/2021/10/19/d0da9fed9d8cdd7100547a4b6dd92f83.writebug)
**内存监视线程流程图**
![](http://www.writebug.com/myres/static/uploads/2021/10/19/c4bfb3d05d465906191d46e8f07f969e.writebug)
# 三、详细设计
## 3.1 设计思想
进程的虚拟地址空间中也有三种状态的页面:空闲页面、保留页面和提交页面。空闲(Free)页面:空闲页面是指那些可以保留或提交的可用页面。保留(Reserved)页面:保留页面是逻辑页面已分配但没有分配物理存储的页面。设置这种状态的效果是可以保留一部分虚拟地址,这样,如果不预先释放这些地址,就不能被其他应用程序(如 Malloc,LocalAlloc 等)的操作所使用。试图读或写空闲页面或保留页面将导致页面出错异常。保留页面可被释放或提交。提交(Committed)页面:提交页面是物理存储(在内存中或磁盘上)已被分配的页面。可对它加以保护,不许访问或允许只读访问,或允许读写访问。提交也可以被回收以释放存储空间,从而变成保留页面。
在本项目中,首先执行Main类中main函数的makeOperationFile()方法生成随机输入文件,其中包含对内存要求作的各种操作;然后执行Main类中main函数的memoryOperation()方法,实现输入文件所要求的各项内存管理操作。
## 3.2 模块设计
### 3.2.1项目包类图
![](http://www.writebug.com/myres/static/uploads/2021/10/19/092a9c6a42e7b3fa34a9561afdd4f372.writebug)
### 3.2.2 成员类说明
- Main:程序入口,执行代码,主要有生成操作文件和执行内存分配程序两个入口
- MemoryBlock:内存块类,模拟代表内存分配的内存块,一个内存块中有多个内存页,记录着内存块起始地址,内存页数,内存页区域,操作权限和锁定状态
- MemoryHandler:内存分配处理类:通过对模拟内存块和模拟内存页的操作,模拟实现内存保留、提交、锁定、解锁、回收、释放六个操作
- MemoryMonitorThread:内存监听线程,和内存分配线程同步执行,实时输出内存分配状态到文件中
- MemoryPage:模拟内存页,包含了每个页面的内存大小,默认4K
- MemoryThread:内存分配线程,和内存监视线程同步,通过读取操作文件中的操作记录实现对模拟内存的分配操作
- Mutex:内存分配线程和内存监视线程的同步信号量
- Operation:内存分配操作参数,包括内存操作大小,等待时间,操作权限,操作类型
- Trace:内存操作返回状态信息类,包括当前处理内存的虚拟地址和物理地址
- VirtualAddress:虚拟地址类,模拟实现内存的虚拟分页页号地址,包括地址其实页号,内存块页总数,内存块对应物理起始地址,操作权限,锁定状态,内存页空间
### 3.2.3 内存分配线程主要执行代码:
```java
@Override
public void run() {
MemoryHandler memory = MemoryHandler.getInstance();
for (int i = 0; i < operations.size(); i++) {
synchronized (mutex) {
// 信号量不为内存操作标记,等待
while (mutex.mutex != Mutex.MUTEX_MEMORY) {
try {
mutex.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Operation operation = operations.get(i);
// 等待
waitForTime(operation.getTime());
// 根据不同操作类型进行相应内存操作
switch (operation.getType()) {
case 0:// 保留区域
// 操作
traces[i % 5].virtualAddress = memory.reserveMemory(
operation.getBlock(), operation.getPermision());
// 输出相关信息
System.out.println("保留区域");
System.out.println(operation.toString());
System.out.println("开始页号:"+ traces[i % 5].virtualAddress .
.getStartPage() + ",结束页号:" + (traces[i % 5].virtualAddress.getStartPage() + traces[i % 5].virtualAddress.getBlock() - 1));
System.out.println("\n");
break;
case 1:// 提交区域
if (traces[i % 5].virtualAddress != null) {
// 操作
traces[i % 5].memoryBlock = memory.commitMemory(traces[i % 5].virtualAddress.getStartPage());
// 输出相关信息
System.out.println("提交区域");
System.out.println(operation.toString());
System.out.println("开始地址:"
+ traces[i % 5].memoryBlock.getStartAddress()
+ ",结束地址:"
+ (traces[i % 5].memoryBlock.getStartAddress()
+ traces[i % 5].memoryBlock.getBlock()
* MemoryPage.PAGE_SIZE - 1));
System.out.println("\n");
} else
System.out.println("当前没有待操作的虚拟内存地址!");
break;
case 2:// 锁定区域
if (traces[i % 5].virtualAddress != null) {
// 操作
memory.lockMemory(traces[i % 5].virtualAddress.getStartPage());
// 输出相关信息
System.out.println("锁定区域");
System.out.println(operation.toString());
System.out.println("开始地址:"
+ traces[i % 5].memoryBlock.getStartAddress()
+ ",结束地址:"
+ (traces[i % 5].memoryBlock.getStartAddress()
+ traces[i % 5].memoryBlock.getBlock()
* MemoryPage.PAGE_SIZE - 1));
System.out.println("\n");
} else
System.out.println("当前没有待操作的虚拟内存地址!");
break;
case 3:// 解锁区域
if (traces[i % 5].virtualAddress != null) {
// 操作
memory.unlockMemory(traces[i % 5].virtualAddress.getStartPage());
// 输出相关信息
System.out.println("解锁区域");
System.out.println(operation.toString());
System.out.println("开始地址:"
+ traces[i % 5].memoryBlock.getStartAddress()
+ ",结束地址:"
+ (traces[i % 5].memoryBlock.getStartAddress()
+ traces[i % 5].memoryBlock.getBlock()
* MemoryPage.PAGE_SIZE - 1));
System.out.println("\n");
} else
System.out.println("当前没有待操作的虚拟内存地址!");
break;
case 4:// 回收区域
if (traces[i % 5].virtualAddress != null) {