深入理解操作系统内存管理
深入理解操作系统内存管理
1. 内存管理深度解析
1.1 内存管理的核心价值与挑战
- 地址空间隔离:确保进程间内存访问的隔离性与安全性
- 资源高效利用:通过虚拟内存技术扩展可用内存空间
- 访问性能优化:减少地址转换和内存访问的开销
- 碎片化治理:有效处理内部碎片和外部碎片问题
1.2 内存层次结构与性能特征
* 内存层次结构性能分析
* 展示不同存储层次的访问时间差异
*/
public class MemoryHierarchyAnalysis {
private static final int ITERATIONS = 1000000;
public static void analyzeMemoryPerformance() {
// L1/L2缓存级别访问
int[] l1Cache = new int[64 * 1024]; // 64KB - 典型L1缓存大小
int[] l2Cache = new int[512 * 1024]; // 512KB - 典型L2缓存大小
int[] mainMemory = new int[10 * 1024 * 1024]; // 10MB - 主存
System.out.println("=== 内存层次结构性能分析 ===");
// L1缓存访问时间
long startTime = System.nanoTime();
for (int i = 0; i < ITERATIONS; i++) {
l1Cache[(i * 16) % l1Cache.length] = i; // 步长16字节避免预取影响
}
long l1Time = System.nanoTime() - startTime;
// L2缓存访问时间
startTime = System.nanoTime();
for (int i = 0; i < ITERATIONS; i++) {
l2Cache[(i * 16) % l2Cache.length] = i;
}
long l2Time = System.nanoTime() - startTime;
// 主存访问时间
startTime = System.nanoTime();
for (int i = 0; i < ITERATIONS; i++) {
mainMemory[(i * 16) % mainMemory.length] = i;
}
long mainMemoryTime = System.nanoTime() - startTime;
System.out.printf("L1 Cache访问时间: %.3f ns/次\n", (double)l1Time / ITERATIONS);
System.out.printf("L2 Cache访问时间: %.3f ns/次\n", (double)l2Time / ITERATIONS);
System.out.printf("主存访问时间: %.3f ns/次\n", (double)mainMemoryTime / ITERATIONS);
System.out.printf("性能比例(L1:主存): 1:%.1f\n",
(double)mainMemoryTime / l1Time);
}
public static void main(String[] args) {
analyzeMemoryPerformance();
}
}
2. 高级内存分配算法实现
2.1 伙伴系统(Buddy System)分配器
import java.util.*;
/**
* 伙伴系统内存分配器
* 实现2的幂次方内存分配,减少外部碎片
*/
public class BuddyMemoryAllocator {
private final int totalSize;
private final int minBlockSize;
private final TreeMap<Integer, TreeSet<Integer>> freeLists;
private final Map<Integer, Integer> allocatedBlocks;
public BuddyMemoryAllocator(int totalSize, int minBlockSize) {
// 确保总大小是2的幂次方
this.totalSize = roundToPowerOfTwo(totalSize);
this.minBlockSize = roundToPowerOfTwo(minBlockSize);
this.freeLists = new TreeMap<>();
this.allocatedBlocks = new HashMap<>();
initializeFreeLists();
}
private int roundToPowerOfTwo(int size) {
int power = 1;
while (power < size) {
power <<= 1;
}
return power;
}
private void initializeFreeLists() {
// 初始化最大块
freeLists.put(totalSize, new TreeSet<>());
freeLists.get(totalSize).add(0);
}
/**
* 分配指定大小的内存块
*/
public Integer allocate(int requestSize) {
if (requestSize <= 0 || requestSize > totalSize) {
return null;
}
int requiredSize = roundToPowerOfTwo(Math.max(requestSize, minBlockSize));
// 查找合适大小的空闲块
Integer blockSize = freeLists.ceilingKey(requiredSize);
if (blockSize == null) {
return null; // 内存不足
}
// 获取该大小的第一个空闲块
TreeSet<Integer> blocks = freeLists.get(blockSize);
if (blocks.isEmpty()) {
return null;
}
int blockAddress = blocks.first();
blocks.remove(blockAddress);
// 如果块太大,则进行分割
while (blockSize > requiredSize) {
blockSize >>= 1; // 分割为两个伙伴块
freeLists.computeIfAbsent(blockSize, k -> new TreeSet<>())
.add(blockAddress + blockSize);
}
allocatedBlocks.put(blockAddress, blockSize);
return blockAddress;
}
/**
* 释放内存块
*/
public void deallocate(int address) {
Integer blockSize = allocatedBlocks.remove(address);
if (blockSize == null) {
throw new IllegalArgumentException("Invalid address: " + address);
}
// 递归合并伙伴块
mergeBuddies(address, blockSize);
}
private void mergeBuddies(int address, int size) {
int currentAddress = address;
int currentSize = size;
while (currentSize < totalSize) {
int buddyAddress = currentAddress ^ currentSize; // 计算伙伴地址
TreeSet<Integer> buddyList = freeLists.get(currentSize);
if (buddyList != null && buddyList.contains(buddyAddress)) {
// 伙伴块也是空闲的,可以合并
buddyList.remove(buddyAddress);
currentAddress = Math.min(currentAddress, buddyAddress);
currentSize <<= 1; // 合并后大小翻倍
} else {
break;
}
}
// 将合并后的块加入空闲列表
freeLists.computeIfAbsent(currentSize, k -> new TreeSet<>())
.add(currentAddress);
}
/**
* 显示内存状态
*/
public void displayMemoryStatus() {
System.out.println("\n=== 伙伴系统内存状态 ===");
System.out.println("总内存大小: " + totalSize + " bytes");
System.out.println("最小块大小: " + minBlockSize + " bytes");
System.out.println("\n空闲块列表:");
for (Map.Entry<Integer, TreeSet<Integer>> entry : freeLists.entrySet()) {
if (!entry.getValue().isEmpty()) {
System.out.printf("大小 %4d: %s\n", entry.getKey(), entry.getValue());
}
}
System.out.println("\n已分配块:");
for (Map.Entry<Integer, Integer> entry : allocatedBlocks.entrySet()) {
System.out.printf("地址 %4d: 大小 %4d\n", entry.getKey(), entry.getValue());
}
}
// 测试代码
public static void main(String[] args) {
BuddyMemoryAllocator allocator = new BuddyMemoryAllocator(1024, 64);
// 测试分配
Integer addr1 = allocator.allocate(100);
Integer addr2 = allocator.allocate(200);
Integer addr3 = allocator.allocate(150);
allocator.displayMemoryStatus();
// 测试释放和合并
if (addr2 != null) {
allocator.deallocate(addr2);
System.out.println("\n释放块: " + addr2);
allocator.displayMemoryStatus();
}
// 测试分配触发合并
Integer addr4 = allocator.allocate(300);
System.out.println("\n分配300字节: " + addr4);
allocator.displayMemoryStatus();
}
}
2.2 Slab分配器实现
import java.util.*;
/**
* Slab分配器 - 专为小对象分配优化
*/
public class SlabAllocator {
private final int slabSize;
private final int objectSize;
private final List<Slab> slabs;
private final Queue<Slab> partialSlabs;
private final Queue<Slab> emptySlabs;
static class Slab {
final byte[] memory;
final BitMap bitmap;
int usedObjects;
Slab(int slabSize, int objectSize) {
this.memory = new byte[slabSize];
int objectCount = slabSize / objectSize;
this.bitmap = new BitMap(objectCount);
this.usedObjects = 0;
}
Integer allocate() {
int index = bitmap.findFirstFree();
if (index != -1) {
bitmap.set(index);
usedObjects++;
return index * objectSize;
}
return null;
}
void free(int offset) {
int index = offset / objectSize;
bitmap.clear(index);
usedObjects--;
}
boolean isFull() {
return usedObjects == bitmap.size();
}
boolean isEmpty() {
return usedObjects == 0;
}
}
/**
* 位图实现,用于跟踪对象分配状态
*/
static class BitMap {
private final byte[] bits;
private final int size;
BitMap(int size) {
this.size = size;
this.bits = new byte[(size + 7) / 8];
}
void set(int index) {
int byteIndex = index / 8;
int bitIndex = index % 8;
bits[byteIndex] |= (1 << bitIndex);
}
void clear(int index) {
int byteIndex = index / 8;
int bitIndex = index % 8;
bits[byteIndex] &= ~(1 << bitIndex);
}
boolean isSet(int index) {
int byteIndex = index / 8;
int bitIndex = index % 8;
return (bits[byteIndex] & (1 << bitIndex)) != 0;
}
int findFirstFree() {
for (int i = 0; i < bits.length; i++) {
if (bits[i] != (byte)0xFF) { // 不是全1
for (int j = 0; j < 8; j++) {
int index = i * 8 + j;
if (index < size && !isSet(index)) {
return index;
}
}
}
}
return -1;
}
int size() {
return size;
}
}
public SlabAllocator(int slabSize, int objectSize) {
if (slabSize % objectSize != 0) {
throw new IllegalArgumentException("Slab size must be multiple of object size");
}
this.slabSize = slabSize;
this.objectSize = objectSize;
this.slabs = new ArrayList<>();
this.partialSlabs = new LinkedList<>();
this.emptySlabs = new LinkedList<>();
// 预分配一个slab
addNewSlab();
}
private void addNewSlab() {
Slab slab = new Slab(slabSize, objectSize);
slabs.add(slab);
emptySlabs.add(slab);
}
/**
* 分配对象
*/
public synchronized Integer allocate() {
// 首先尝试部分填充的slab
Slab slab = partialSlabs.poll();
if (slab != null) {
Integer result = slab.allocate();
if (result != null) {
if (slab.isFull()) {
// slab已满,不需要放回队列
} else {
partialSlabs.offer(slab);
}
return System.identityHashCode(slab) + result; // 返回唯一地址
}
}
// 尝试空slab
slab = emptySlabs.poll();
if (slab != null) {
Integer result = slab.allocate();
if (result != null) {
partialSlabs.offer(slab);
return System.identityHashCode(slab) + result;
}
}
// 需要新slab
addNewSlab();
return allocate(); // 递归调用
}
/**
* 释放对象
*/
public synchronized void free(int address) {
for (Slab slab : slabs) {
int slabId = System.identityHashCode(slab);
if (address >= slabId && address < slabId + slabSize) {
int offset = address - slabId;
slab.free(offset);
// 更新slab队列状态
if (slab.isEmpty()) {
partialSlabs.remove(slab);
emptySlabs.offer(slab);
} else if (!partialSlabs.contains(slab)) {
partialSlabs.offer(slab);
}
return;
}
}
throw new IllegalArgumentException("Invalid address: " + address);
}
/**
* 显示分配器状态
*/
public void displayStatus() {
System.out.println("\n=== Slab分配器状态 ===");
System.out.printf("Slab大小: %d, 对象大小: %d, 每Slab对象数: %d\n",
slabSize, objectSize, slabSize / objectSize);
System.out.printf("Slab总数: %d, 部分填充: %d, 空Slab: %d\n",
slabs.size(), partialSlabs.size(), emptySlabs.size());
for (int i = 0; i < slabs.size(); i++) {
Slab slab = slabs.get(i);
System.out.printf("Slab %d: 使用率 %d/%d (%.1f%%)\n",
i, slab.usedObjects, slab.bitmap.size(),
(slab.usedObjects * 100.0) / slab.bitmap.size());
}
}
public static void main(String[] args) {
SlabAllocator allocator = new SlabAllocator(4096, 64); // 4KB slab, 64B对象
List<Integer> addresses = new ArrayList<>();
// 分配测试
for (int i = 0; i < 100; i++) {
Integer addr = allocator.allocate();
if (addr != null) {
addresses.add(addr);
}
}
allocator.displayStatus();
// 释放部分对象
for (int i = 0; i < 30; i++) {
if (!addresses.isEmpty()) {
allocator.free(addresses.remove(0));
}
}
System.out.println("\n释放30个对象后:");
allocator.displayStatus();
}
}
3. 高级页面置换算法
3.1 二次机会(Clock)算法
import java.util.*;
/**
* Clock页面置换算法
* LRU的高效近似实现
*/
public class ClockPageReplacement {
private final int capacity;
private final Page[] pages;
private final Map<Integer, Integer> pageToIndex;
private int clockHand;
static class Page {
int pageNumber;
boolean referenceBit;
boolean modifiedBit;
Page(int pageNumber) {
this.pageNumber = pageNumber;
this.referenceBit = false;
this.modifiedBit = false;
}
}
public ClockPageReplacement(int capacity) {
this.capacity = capacity;
this.pages = new Page[capacity];
this.pageToIndex = new HashMap<>();
this.clockHand = 0;
}
/**
* 访问页面
*/
public void accessPage(int pageNumber, boolean isWrite) {
if (pageToIndex.containsKey(pageNumber)) {
// 页面命中
int index = pageToIndex.get(pageNumber);
pages[index].referenceBit = true;
if (isWrite) {
pages[index].modifiedBit = true;
}
System.out.printf("页面 %d 命中 (引用位: %s, 修改位: %s)\n",
pageNumber, pages[index].referenceBit, pages[index].modifiedBit);
} else {
// 页面错误,需要置换
handlePageFault(pageNumber, isWrite);
}
}
private void handlePageFault(int pageNumber, boolean isWrite) {
System.out.printf("页面 %d 错误 - ", pageNumber);
while (true) {
Page current = pages[clockHand];
if (current == null) {
// 空槽位
pages[clockHand] = new Page(pageNumber);
pages[clockHand].referenceBit = true;
if (isWrite) {
pages[clockHand].modifiedBit = true;
}
pageToIndex.put(pageNumber, clockHand);
System.out.printf("加载到空闲帧 %d\n", clockHand);
clockHand = (clockHand + 1) % capacity;
return;
}
if (!current.referenceBit) {
// 找到置换候选
int oldPage = current.pageNumber;
pageToIndex.remove(oldPage);
System.out.printf("置换页面 %d (引用位: %s, 修改位: %s) ",
oldPage, current.referenceBit, current.modifiedBit);
if (current.modifiedBit) {
System.out.print("[需要写回磁盘] ");
}
current.pageNumber = pageNumber;
current.referenceBit = true;
current.modifiedBit = isWrite;
pageToIndex.put(pageNumber, clockHand);
System.out.printf("-> 帧 %d\n", clockHand);
clockHand = (clockHand + 1) % capacity;
return;
} else {
// 给第二次机会,清除引用位
current.referenceBit = false;
System.out.printf("给页面 %d 第二次机会 ", current.pageNumber);
}
clockHand = (clockHand + 1) % capacity;
}
}
/**
* 显示内存状态
*/
public void displayMemory() {
System.out.println("\n=== Clock算法内存状态 ===");
System.out.println("时钟指针位置: " + clockHand);
System.out.println("帧\t页面\t引用位\t修改位");
for (int i = 0; i < capacity; i++) {
if (pages[i] != null) {
System.out.printf("%d\t%d\t%s\t%s%s\n",
i, pages[i].pageNumber,
pages[i].referenceBit, pages[i].modifiedBit,
(i == clockHand) ? " <-- 指针" : "");
} else {
System.out.printf("%d\t空\t-\t-%s\n", i, (i == clockHand) ? " <-- 指针" : "");
}
}
System.out.println();
}
public static void main(String[] args) {
ClockPageReplacement clock = new ClockPageReplacement(4);
int[] accesses = {1, 2, 3, 4, 1, 5, 6, 2, 1, 2, 3, 7, 8, 1};
System.out.println("页面访问序列: " + Arrays.toString(accesses));
for (int i = 0; i < accesses.length; i++) {
System.out.printf("\n步骤 %d: ", i + 1);
clock.accessPage(accesses[i], i % 3 == 0); // 每3次访问有一次写操作
clock.displayMemory();
}
}
}
3.2 工作集页面置换算法
import java.util.*;
/**
* 工作集模型页面置换
* 基于时间局部性原理
*/
public class WorkingSetPageReplacement {
private final int capacity;
private final Map<Integer, Long> pageToLastAccess;
private final long tau; // 工作集窗口大小
public WorkingSetPageReplacement(int capacity, long tau) {
this.capacity = capacity;
this.pageToLastAccess = new LinkedHashMap<>(capacity, 0.75f, true);
this.tau = tau;
}
/**
* 访问页面
*/
public void accessPage(int pageNumber) {
long currentTime = System.currentTimeMillis();
if (pageToLastAccess.containsKey(pageNumber)) {
// 页面命中,更新访问时间
pageToLastAccess.put(pageNumber, currentTime);
System.out.printf("页面 %d 命中\n", pageNumber);
} else {
// 页面错误
if (pageToLastAccess.size() >= capacity) {
// 需要置换
evictOutOfWorkingSet(currentTime);
}
pageToLastAccess.put(pageNumber, currentTime);
System.out.printf("页面 %d 加载\n", pageNumber);
}
displayWorkingSet(currentTime);
}
/**
* 移除不在工作集中的页面
*/
private void evictOutOfWorkingSet(long currentTime) {
Iterator<Map.Entry<Integer, Long>> iterator = pageToLastAccess.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<Integer, Long> entry = iterator.next();
if (currentTime - entry.getValue() > tau) {
// 页面不在工作集中
System.out.printf("移除不在工作集中的页面: %d (最后访问: %dms前)\n",
entry.getKey(), currentTime - entry.getValue());
iterator.remove();
return;
}
}
// 如果所有页面都在工作集中,移除最久未访问的
Map.Entry<Integer, Long> oldest = pageToLastAccess.entrySet().iterator().next();
System.out.printf("工作集满,移除最久未访问页面: %d\n", oldest.getKey());
pageToLastAccess.remove(oldest.getKey());
}
/**
* 显示工作集状态
*/
private void displayWorkingSet(long currentTime) {
System.out.print("当前工作集: {");
for (Map.Entry<Integer, Long> entry : pageToLastAccess.entrySet()) {
long age = currentTime - entry.getValue();
System.out.printf(" %d(%dms)", entry.getKey(), age);
}
System.out.println(" }");
System.out.printf("工作集大小: %d/%d\n\n", pageToLastAccess.size(), capacity);
}
/**
* 获取工作集大小统计
*/
public void printWorkingSetStats() {
long currentTime = System.currentTimeMillis();
int inWorkingSet = 0;
for (long accessTime : pageToLastAccess.values()) {
if (currentTime - accessTime <= tau) {
inWorkingSet++;
}
}
System.out.printf("工作集统计: 总页面=%d, 工作集中=%d, 占比=%.1f%%\n",
pageToLastAccess.size(), inWorkingSet,
(inWorkingSet * 100.0) / pageToLastAccess.size());
}
public static void main(String[] args) throws InterruptedException {
WorkingSetPageReplacement ws = new WorkingSetPageReplacement(4, 1000); // 1秒工作集窗口
// 模拟访问模式:局部性访问
int[] pattern1 = {1, 2, 3, 4, 1, 2, 3, 4};
int[] pattern2 = {5, 6, 5, 6, 5, 6};
int[] pattern3 = {1, 2, 7, 8, 1, 2};
System.out.println("=== 模式1访问 ===");
for (int page : pattern1) {
ws.accessPage(page);
Thread.sleep(200);
}
System.out.println("=== 模式2访问 ===");
for (int page : pattern2) {
ws.accessPage(page);
Thread.sleep(200);
}
System.out.println("=== 模式3访问(返回模式1) ===");
for (int page : pattern3) {
ws.accessPage(page);
Thread.sleep(200);
}
ws.printWorkingSetStats();
}
}
4. 虚拟内存与TLB模拟
4.1 TLB(转换检测缓冲区)模拟
import java.util.*;
/**
* TLB模拟器
* 加速虚拟地址到物理地址的转换
*/
public class TLBSimulator {
private final int tlbSize;
private final Map<Integer, TLBEntry> tlb;
private int hitCount;
private int missCount;
static class TLBEntry {
int virtualPage;
int physicalFrame;
long accessTime;
boolean dirty;
TLBEntry(int virtualPage, int physicalFrame) {
this.virtualPage = virtualPage;
this.physicalFrame = physicalFrame;
this.accessTime = System.nanoTime();
this.dirty = false;
}
void updateAccess(boolean isWrite) {
this.accessTime = System.nanoTime();
if (isWrite) {
this.dirty = true;
}
}
}
public TLBSimulator(int size) {
this.tlbSize = size;
this.tlb = new LinkedHashMap<>(size, 0.75f, true);
this.hitCount = 0;
this.missCount = 0;
}
/**
* TLB查找
*/
public Integer lookup(int virtualPage, boolean isWrite) {
TLBEntry entry = tlb.get(virtualPage);
if (entry != null) {
// TLB命中
entry.updateAccess(isWrite);
hitCount++;
return entry.physicalFrame;
} else {
// TLB未命中
missCount++;
return null;
}
}
/**
* 添加TLB条目
*/
public void addEntry(int virtualPage, int physicalFrame, boolean isWrite) {
if (tlb.size() >= tlbSize) {
// 使用LRU策略淘汰
evictLRU();
}
TLBEntry entry = new TLBEntry(virtualPage, physicalFrame);
entry.updateAccess(isWrite);
tlb.put(virtualPage, entry);
}
/**
* 淘汰最近最少使用的条目
*/
private void evictLRU() {
Iterator<Map.Entry<Integer, TLBEntry>> iterator = tlb.entrySet().iterator();
if (iterator.hasNext()) {
Map.Entry<Integer, TLBEntry> lru = iterator.next();
System.out.printf("TLB淘汰: 虚拟页 %d -> 物理帧 %d %s\n",
lru.getValue().virtualPage, lru.getValue().physicalFrame,
lru.getValue().dirty ? "(脏)" : "");
iterator.remove();
}
}
/**
* 获取TLB命中率
*/
public double getHitRatio() {
int total = hitCount + missCount;
return total > 0 ? (double) hitCount / total : 0.0;
}
/**
* 显示TLB状态
*/
public void displayTLBStatus() {
System.out.println("\n=== TLB状态 ===");
System.out.printf("TLB大小: %d, 使用: %d\n", tlbSize, tlb.size());
System.out.printf("命中: %d, 未命中: %d, 命中率: %.2f%%\n",
hitCount, missCount, getHitRatio() * 100);
System.out.println("TLB内容:");
System.out.println("虚拟页\t物理帧\t最后访问\t脏位");
for (TLBEntry entry : tlb.values()) {
System.out.printf("%d\t%d\t%d\t%s\n",
entry.virtualPage, entry.physicalFrame,
System.nanoTime() - entry.accessTime, entry.dirty);
}
}
/**
* 完整的地址转换模拟
*/
public static class CompleteAddressTranslation {
private final TLBSimulator tlb;
private final Map<Integer, Integer> pageTable;
private final Set<Integer> physicalFrames;
private final int pageSize;
public CompleteAddressTranslation(int tlbSize, int physicalMemory, int pageSize) {
this.tlb = new TLBSimulator(tlbSize);
this.pageTable = new HashMap<>();
this.physicalFrames = new HashSet<>();
this.pageSize = pageSize;
// 初始化物理帧
int frameCount = physicalMemory / pageSize;
for (int i = 0; i < frameCount; i++) {
physicalFrames.add(i);
}
}
public int translateAddress(int virtualAddress, boolean isWrite) {
int virtualPage = virtualAddress / pageSize;
int offset = virtualAddress % pageSize;
// 首先尝试TLB
Integer physicalFrame = tlb.lookup(virtualPage, isWrite);
if (physicalFrame == null) {
// TLB未命中,查找页表
physicalFrame = pageTable.get(virtualPage);
if (physicalFrame == null) {
// 页表未命中,处理缺页
physicalFrame = handlePageFault(virtualPage);
}
// 更新TLB
tlb.addEntry(virtualPage, physicalFrame, isWrite);
}
int physicalAddress = physicalFrame * pageSize + offset;
System.out.printf("虚拟地址 %d (页 %d, 偏移 %d) -> 物理地址 %d (帧 %d)\n",
virtualAddress, virtualPage, offset, physicalAddress, physicalFrame);
return physicalAddress;
}
private Integer handlePageFault(int virtualPage) {
System.out.printf("缺页错误: 虚拟页 %d\n", virtualPage);
if (physicalFrames.isEmpty()) {
// 需要页面置换(简化实现)
System.out.println("需要页面置换...");
// 实际实现中这里会调用页面置换算法
return 0; // 简化返回第一个帧
}
// 分配空闲帧
Integer frame = physicalFrames.iterator().next();
physicalFrames.remove(frame);
pageTable.put(virtualPage, frame);
System.out.printf("分配物理帧 %d 给虚拟页 %d\n", frame, virtualPage);
return frame;
}
public void displayStats() {
tlb.displayTLBStatus();
System.out.printf("\n页表大小: %d, 空闲物理帧: %d\n",
pageTable.size(), physicalFrames.size());
}
}
public static void main(String[] args) {
CompleteAddressTranslation cat = new CompleteAddressTranslation(4, 16384, 4096); // 16KB物理内存,4KB页
// 模拟地址访问序列
int[] addresses = {0x1000, 0x2000, 0x3000, 0x1004, 0x4000, 0x2004, 0x5000, 0x1008};
for (int addr : addresses) {
System.out.printf("\n访问地址: 0x%04X\n", addr);
cat.translateAddress(addr, false);
}
cat.displayStats();
}
}
5. 内存压缩与去重技术
5.1 内存页面压缩模拟
import java.util.*;
import java.util.zip.*;
/**
* 内存页面压缩技术模拟
* 通过压缩减少内存占用
*/
public class MemoryCompression {
private final Map<Integer, CompressedPage> compressedPages;
private final int originalPageSize;
private long totalCompressedSize;
private long totalOriginalSize;
static class CompressedPage {
byte[] compressedData;
int originalSize;
long lastAccessTime;
int accessCount;
CompressedPage(byte[] compressedData, int originalSize) {
this.compressedData = compressedData;
this.originalSize = originalSize;
this.lastAccessTime = System.currentTimeMillis();
this.accessCount = 1;
}
void updateAccess() {
this.lastAccessTime = System.currentTimeMillis();
this.accessCount++;
}
double getCompressionRatio() {
return (double) compressedData.length / originalSize;
}
}
public MemoryCompression(int pageSize) {
this.compressedPages = new HashMap<>();
this.originalPageSize = pageSize;
this.totalCompressedSize = 0;
this.totalOriginalSize = 0;
}
/**
* 压缩页面
*/
public int compressPage(int pageId, byte[] pageData) {
try {
byte[] compressed = compressData(pageData);
CompressedPage compressedPage = new CompressedPage(compressed, pageData.length);
compressedPages.put(pageId, compressedPage);
totalCompressedSize += compressed.length;
totalOriginalSize += pageData.length;
double ratio = (double) compressed.length / pageData.length;
System.out.printf("页面 %d 压缩: %d -> %d bytes (比率: %.2f)\n",
pageId, pageData.length, compressed.length, ratio);
return compressed.length;
} catch (Exception e) {
System.out.printf("页面 %d 压缩失败: %s\n", pageId, e.getMessage());
return pageData.length; // 返回原始大小
}
}
/**
* 解压缩页面
*/
public byte[] decompressPage(int pageId) {
CompressedPage compressedPage = compressedPages.get(pageId);
if (compressedPage == null) {
throw new IllegalArgumentException("Page not found: " + pageId);
}
compressedPage.updateAccess();
try {
byte[] decompressed = decompressData(compressedPage.compressedData,
compressedPage.originalSize);
System.out.printf("页面 %d 解压缩: %d -> %d bytes (访问次数: %d)\n",
pageId, compressedPage.compressedData.length,
decompressed.length, compressedPage.accessCount);
return decompressed;
} catch (Exception e) {
System.out.printf("页面 %d 解压缩失败: %s\n", pageId, e.getMessage());
return new byte[compressedPage.originalSize];
}
}
private byte[] compressData(byte[] data) throws Exception {
Deflater deflater = new Deflater(Deflater.BEST_COMPRESSION);
deflater.setInput(data);
deflater.finish();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(data.length);
byte[] buffer = new byte[1024];
while (!deflater.finished()) {
int count = deflater.deflate(buffer);
outputStream.write(buffer, 0, count);
}
outputStream.close();
return outputStream.toByteArray();
}
private byte[] decompressData(byte[] compressedData, int originalSize) throws Exception {
Inflater inflater = new Inflater();
inflater.setInput(compressedData);
byte[] result = new byte[originalSize];
int resultLength = inflater.inflate(result);
inflater.end();
return Arrays.copyOf(result, resultLength);
}
/**
* 显示压缩统计
*/
public void displayCompressionStats() {
double overallRatio = (double) totalCompressedSize / totalOriginalSize;
double spaceSaving = (1 - overallRatio) * 100;
System.out.println("\n=== 内存压缩统计 ===");
System.out.printf("总原始大小: %d bytes\n", totalOriginalSize);
System.out.printf("总压缩大小: %d bytes\n", totalCompressedSize);
System.out.printf("整体压缩比率: %.2f\n", overallRatio);
System.out.printf("空间节省: %.1f%%\n", spaceSaving);
System.out.printf("压缩页面数: %d\n", compressedPages.size());
// 显示压缩效果最好的页面
compressedPages.entrySet().stream()
.sorted(Comparator.comparingDouble(e -> e.getValue().getCompressionRatio()))
.limit(5)
.forEach(e -> System.out.printf("页面 %d: 比率 %.2f, 访问次数 %d\n",
e.getKey(), e.getValue().getCompressionRatio(), e.getValue().accessCount));
}
/**
* 生成测试数据(包含重复模式)
*/
private static byte[] generateTestData(int size, int pattern) {
byte[] data = new byte[size];
Random random = new Random(pattern);
// 生成有重复模式的数据,便于压缩
for (int i = 0; i < size; i++) {
if (i % 16 == 0) {
// 每16字节插入一个模式
byte patternByte = (byte) (pattern & 0xFF);
Arrays.fill(data, i, Math.min(i + 8, size), patternByte);
i += 7;
} else {
data[i] = (byte) random.nextInt(256);
}
}
return data;
}
public static void main(String[] args) {
MemoryCompression compressor = new MemoryCompression(4096);
// 测试压缩多个页面
for (int i = 0; i < 10; i++) {
byte[] testData = generateTestData(4096, i % 3); // 3种模式循环
compressor.compressPage(i, testData);
}
// 测试解压缩
for (int i = 0; i < 3; i++) {
compressor.decompressPage(i);
}
compressor.displayCompressionStats();
}
}
6. 性能优化与最佳实践
6.1 内存访问模式优化
* 内存访问模式性能分析
* 展示不同访问模式对性能的影响
*/
public class MemoryAccessPattern {
private static final int SIZE = 1000000;
private static final int[] array = new int[SIZE];
static {
// 初始化数组
for (int i = 0; i < SIZE; i++) {
array[i] = i;
}
}
/**
* 顺序访问 - 缓存友好
*/
public static long sequentialAccess() {
long sum = 0;
long startTime = System.nanoTime();
for (int i = 0; i < SIZE; i++) {
sum += array[i];
}
long endTime = System.nanoTime();
System.out.printf("顺序访问: %d ns, 总和: %d\n", endTime - startTime, sum);
return endTime - startTime;
}
/**
* 随机访问 - 缓存不友好
*/
public static long randomAccess() {
long sum = 0;
Random random = new Random(42); // 固定种子保证可重复性
long startTime = System.nanoTime();
for (int i = 0; i < SIZE; i++) {
int index = random.nextInt(SIZE);
sum += array[index];
}
long endTime = System.nanoTime();
System.out.printf("随机访问: %d ns, 总和: %d\n", endTime - startTime, sum);
return endTime - startTime;
}
/**
* 跨步访问 - 测试不同步长的影响
*/
public static long stridedAccess(int stride) {
long sum = 0;
long startTime = System.nanoTime();
for (int i = 0; i < SIZE; i++) {
int index = (i * stride) % SIZE;
sum += array[index];
}
long endTime = System.nanoTime();
System.out.printf("跨步访问(步长%d): %d ns, 总和: %d\n", stride, endTime - startTime, sum);
return endTime - startTime;
}
/**
* 缓存行大小测试
*/
public static void cacheLineTest() {
final int CACHE_LINE_SIZE = 64; // 典型缓存行大小(字节)
final int INTS_PER_LINE = CACHE_LINE_SIZE / 4; // 每个缓存行的整数数量
System.out.println("\n=== 缓存行性能测试 ===");
// 测试不同步长的性能
for (int stride = 1; stride <= INTS_PER_LINE * 2; stride *= 2) {
long time = stridedAccess(stride);
System.out.printf("步长: %d, 相对性能: %.2fx\n",
stride, (double)time / stridedAccess(1));
}
}
public static void main(String[] args) {
System.out.println("=== 内存访问模式性能比较 ===");
long sequentialTime = sequentialAccess();
long randomTime = randomAccess();
System.out.printf("\n性能比较: 顺序访问比随机访问快 %.2f 倍\n",
(double)randomTime / sequentialTime);
cacheLineTest();
// 预热JVM
for (int i = 0; i < 1000; i++) {
sequentialAccess();
}
// 正式测试
long warmSequential = sequentialAccess();
long warmRandom = randomAccess();
System.out.printf("\n预热后性能比较: 顺序访问比随机访问快 %.2f 倍\n",
(double)warmRandom / warmSequential);
}
}
7. 现代内存管理趋势
7.1 非一致内存访问(NUMA)模拟
import java.util.*;
/**
* NUMA架构内存访问模拟
* 展示不同节点间内存访问的性能差异
*/
public class NUMASimulation {
private final int numNodes;
private final List<NUMANode> nodes;
private final int[][] accessLatency; // 节点间访问延迟矩阵
static class NUMANode {
final int nodeId;
final Map<Integer, Integer> localMemory; // 页号 -> 数据
int localAccessCount;
int remoteAccessCount;
NUMANode(int nodeId) {
this.nodeId = nodeId;
this.localMemory = new HashMap<>();
this.localAccessCount = 0;
this.remoteAccessCount = 0;
}
void storePage(int pageId, int data) {
localMemory.put(pageId, data);
}
Integer accessPage(int pageId, int remoteNodeId) {
if (localMemory.containsKey(pageId)) {
localAccessCount++;
return localMemory.get(pageId);
} else {
remoteAccessCount++;
// 模拟远程访问开销
try {
Thread.sleep(1); // 远程访问延迟
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return null;
}
}
double getLocalAccessRatio() {
int total = localAccessCount + remoteAccessCount;
return total > 0 ? (double) localAccessCount / total : 0.0;
}
}
public NUMASimulation(int numNodes) {
this.numNodes = numNodes;
this.nodes = new ArrayList<>();
this.accessLatency = new int[numNodes][numNodes];
// 初始化节点和延迟矩阵
for (int i = 0; i < numNodes; i++) {
nodes.add(new NUMANode(i));
for (int j = 0; j < numNodes; j++) {
accessLatency[i][j] = (i == j) ? 1 : 1 + Math.abs(i - j); // 简单延迟模型
}
}
}
/**
* 内存访问模拟
*/
public void accessMemory(int requestingNode, int pageId) {
NUMANode node = nodes.get(requestingNode);
Integer result = node.accessPage(pageId, -1);
if (result == null) {
// 需要在其他节点查找
for (int i = 0; i < numNodes; i++) {
if (i != requestingNode) {
result = nodes.get(i).accessPage(pageId, requestingNode);
if (result != null) {
System.out.printf("节点 %d 远程访问节点 %d 的页面 %d (延迟: %d)\n",
requestingNode, i, pageId, accessLatency[requestingNode][i]);
break;
}
}
}
} else {
System.out.printf("节点 %d 本地访问页面 %d\n", requestingNode, pageId);
}
}
/**
* 显示NUMA统计信息
*/
public void displayNUMAStats() {
System.out.println("\n=== NUMA架构统计 ===");
System.out.println("节点\t本地访问\t远程访问\t本地访问率");
for (NUMANode node : nodes) {
System.out.printf("%d\t%d\t%d\t%.1f%%\n",
node.nodeId, node.localAccessCount, node.remoteAccessCount,
node.getLocalAccessRatio() * 100);
}
// 计算整体本地访问率
int totalLocal = nodes.stream().mapToInt(n -> n.localAccessCount).sum();
int totalRemote = nodes.stream().mapToInt(n -> n.remoteAccessCount).sum();
double overallLocalRatio = (double) totalLocal / (totalLocal + totalRemote);
System.out.printf("\n整体本地访问率: %.1f%%\n", overallLocalRatio * 100);
System.out.printf("性能建议: %s\n",
overallLocalRatio > 0.8 ? "良好" :
overallLocalRatio > 0.6 ? "一般" : "需要优化");
}
/**
* 页面迁移优化 - 将频繁访问的页面迁移到访问节点
*/
public void migratePage(int pageId, int fromNode, int toNode) {
NUMANode source = nodes.get(fromNode);
NUMANode target = nodes.get(toNode);
Integer data = source.localMemory.remove(pageId);
if (data != null) {
target.localMemory.put(pageId, data);
System.out.printf("页面 %d 从节点 %d 迁移到节点 %d\n", pageId, fromNode, toNode);
}
}
public static void main(String[] args) {
NUMASimulation numa = new NUMASimulation(4); // 4节点NUMA系统
// 初始化页面分布
Random random = new Random(42);
for (int page = 0; page < 100; page++) {
int node = random.nextInt(4);
numa.nodes.get(node).storePage(page, page * 100);
}
// 模拟访问模式 - 每个节点主要访问特定范围的页面
for (int node = 0; node < 4; node++) {
for (int i = 0; i < 50; i++) {
int page = random.nextInt(25) + node * 25; // 偏向本地访问
numa.accessMemory(node, page);
}
}
numa.displayNUMAStats();
// 执行页面迁移优化
System.out.println("\n=== 执行页面迁移优化 ===");
for (int page = 0; page < 20; page++) {
int preferredNode = page / 25;
numa.migratePage(page, (preferredNode + 1) % 4, preferredNode);
}
// 重新测试
for (int node = 0; node < 4; node++) {
for (int i = 0; i < 50; i++) {
int page = random.nextInt(25) + node * 25;
numa.accessMemory(node, page);
}
}
numa.displayNUMAStats();
}
}
8. 总结与展望
8.1 核心技术要点回顾
- 分配算法演进:从基础的连续分配到伙伴系统、Slab分配器
- 页面置换策略:LRU、Clock、工作集等算法的适用场景
- 性能优化技术:TLB、内存压缩、访问模式优化
- 现代架构特性:NUMA架构的内存访问特性