内存是计算机的一个重要组成部分,它的主要作用在于配合 CPU 的高速运转,使得计算机的运行速度得到大大地提升
我们应该知道,计算机上的一切都是程序,我们使用计算机其实就是在运行计算机上的各种程序,而这些程序都存储在我们的硬盘中(外存),硬盘中的数据内容是几乎可以永久存储的但是 它的读取速度相较于 CPU 的处理速度是十分缓慢的 **
如果没有内存,CPU 在处理完一段程序后,要空闲很长一段时间等待硬盘继续传送数据,这样一来,极大地降低了 CPU 的效率
而内存由于其构造原理与硬盘不同,它的读写速度非常快**(虽然不及 CPU,但可以远高于硬盘),但是它内部的数据断电后就会消失,不具有永久存储性,因此,内存的主要作用就相当于 CPU 与硬盘之间的中转站,内存中会暂存即将要执行的程序,等待着 CPU 的调度
基于内存的工作原理,你一定想问:既然内存速度那么快,为什么不把硬盘中的所有数据全部放入内存等待 CPU 调度呢?
因为内存不仅读取速度是硬盘的很多倍,制造成本也远高于硬盘,所以必须省着点用啊!
那么新的问题来了,内存空间有限,那么在有限的空间中该怎么存取数据呢?这就是内存的分配算法了
内存分配算法,大体来说分为:连续式分配 与 非连续式分配
顾名思义连续式分配就是把所以要执行的程序 完整的,有序的 存入内存,连续式分配又可以分为固定分区分配 和 动态分区分配
非连续式分配就是把要执行的程序按照一定规则进行拆分,显然这样更有效率,现在的操作系统通常也都是采用这种方式分配内存
关于内存的原理部分本文只讲到这里,本文主要关注动态分区分配算法
所谓动态分区分配,就是指内存在初始时不会划分区域,而是会在进程装入时,根据所要装入的进程大小动态地对内存空间进行划分,以提高内存空间利用率,降低碎片的大小
动态分区分配算法有以下四种:
空闲分区以地址递增的次序链接。分配内存时顺序查找,找到大小满足要求的第一个空闲分区就进行分配
又称循环首次适应法,由首次适应法演变而成,不同之处是分配内存时从上一次查找结束的位置开始继续查找
空闲分区按容量递增形成分区链,找到第一个能满足要求的空闲分区就进行分配
又称最大适应算法(Largest Fit),空闲分区以容量递减的次序链接,找到第一个能满足要求的空闲分区(也就是最大的分区)就进行分配
package com.dht.memory;
import java.util.LinkedList;
import java.util.Scanner;
/**
* 内存类
* @author [email protected]
*/
public class Memory{
/**
* 内存大小
*/
private int size;
/**
* 最小剩余分区大小
*/
private static final int MIN_SIZE = 5;
/**
* 内存分区
*/
private LinkedList<Zone> zones;
/**
* 上次分配的空闲区位置
*/
private int pointer;
/**
* 分区节点类
*/
class Zone{
/**
* 分区大小
*/
private int size;
/**
* 分区始址
*/
private int head;
/**
* 空闲状态
*/
private boolean isFree;
public Zone(int head, int size) {
this.head = head;
this.size = size;
this.isFree = true;
}
}
/**
* 默认内存大小为 100 KB
*/
public Memory(){
this.size = 100;
this.pointer = 0;
this.zones = new LinkedList<>();
zones.add(new Zone(0, size));
}
public Memory(int size) {
this.size = size;
this.pointer = 0;
this.zones = new LinkedList<>();
zones.add(new Zone(0, size));
}
/**
* 内存分配
* @param size 指定需要分配的大小
*/
public void allocation(int size){
System.out.println("1.FirstFit 2.NextFit 3.BestFit 4.WorstFit");
System.out.print("请选择分配算法:");
Scanner in = new Scanner(System.in);
int algorithm = in.nextInt();
switch (algorithm){
case 1:
fristFit(size);break;
case 2:
nextFit(size);break;
case 3:
bestFit(size);break;
case 4:
worstFit(size);break;
default:
System.out.println("请重新选择!");
}
}
/**
* 首次适应算法
* @param size 指定需要分配的大小
*/
private void fristFit(int size){
//遍历分区链表
for (pointer = 0; pointer < zones.size(); pointer++){
Zone tmp = zones.get(pointer);
//找到可用分区(空闲且大小足够)
if (tmp.isFree && (tmp.size > size)){
doAllocation(size, pointer, tmp);
return;
}
}
//遍历结束后未找到可用分区, 则内存分配失败
System.out.println("无可用内存空间!");
}
/**
* 循环首次适应算法
* @param size 指定需要分配的大小
*/
private void nextFit(int size){
//从上次分配空闲区位置开始遍历分区链表
Zone tmp = zones.get(pointer);
if (tmp.isFree && (tmp.size > size)){
doAllocation(size, pointer, tmp);
return;
}
int len = zones.size();
int i = (pointer + 1) % len;
for (; i != pointer; i = (i+1) % len){
tmp = zones.get(i);
//找到可用分区(空闲且大小足够)
if (tmp.isFree && (tmp.size > size)){
doAllocation(size, i, tmp);
return;
}
}
//遍历结束后未找到可用分区, 则内存分配失败
System.out.println("无可用内存空间!");
}
/**
* 最佳适应算法
* @param size 指定需要分配的大小
*/
private void bestFit(int size){
int flag = -1;
int min = this.size;
for (pointer = 0; pointer < zones.size(); pointer++){
Zone tmp = zones.get(pointer);
if (tmp.isFree && (tmp.size > size)){
if (min > tmp.size - size){
min = tmp.size - size;
flag = pointer;
}
}
}
if (flag == -1){
System.out.println("无可用内存空间!");
}else {
doAllocation(size, flag, zones.get(flag));
}
}
/**
* 最坏适应算法
* @param size 指定需要分配的大小
*/
private void worstFit(int size){
int flag = -1;
int max = 0;
for (pointer = 0; pointer < zones.size(); pointer++){
Zone tmp = zones.get(pointer);
if (tmp.isFree && (tmp.size > size)){
if (max < tmp.size - size){
max = tmp.size - size;
flag = pointer;
}
}
}
if (flag == -1){
System.out.println("无可用内存空间!");
}else {
doAllocation(size, flag, zones.get(flag));
}
}
/**
* 执行分配
* @param size 申请大小
* @param location 当前可用分区位置
* @param tmp 可用空闲区
*/
private void doAllocation(int size, int location, Zone tmp) {
//如果分割后分区剩余大小过小(MIN_SIZE)则将分区全部分配,否则分割为两个分区
if (tmp.size - size <= MIN_SIZE){
tmp.isFree = false;
} else {
Zone split = new Zone(tmp.head + size, tmp.size - size);
zones.add(location + 1, split);
tmp.size = size;
tmp.isFree = false;
}
System.out.println("成功分配 " + size + "KB 内存!");
}
/**
* 内存回收
* @param id 指定要回收的分区好号
*/
public void collection(int id){
if (id >= zones.size()){
System.out.println("无此分区编号!");
return;
}
Zone tmp = zones.get(id);
int size = tmp.size;
if (tmp.isFree) {
System.out.println("指定分区未被分配, 无需回收");
return;
}
//如果回收分区不是尾分区且后一个分区为空闲, 则与后一个分区合并
if (id < zones.size() - 1 && zones.get(id + 1).isFree){
Zone next = zones.get(id + 1);
tmp.size += next.size;
zones.remove(next);
}
//如果回收分区不是首分区且前一个分区为空闲, 则与前一个分区合并
if (id > 0 && zones.get(id - 1).isFree){
Zone previous = zones.get(id - 1);
previous.size += tmp.size;
zones.remove(id);
id--;
}
zones.get(id).isFree = true;
System.out.println("内存回收成功!, 本次回收了 " + size + "KB 空间!");
}
/**
* 展示内存分区状况
*/
public void showZones(){
System.out.println("------------------------------------");
System.out.println("分区编号\t分区始址\t分区大小\t空闲状态\t");
System.out.println("------------------------------------");
for (int i = 0; i < zones.size(); i++){
Zone tmp = zones.get(i);
System.out.println(i + "\t\t" + tmp.head + "\t\t" +
tmp.size + " \t" + tmp.isFree);
}
System.out.println("------------------------------------");
}
}