分配方式
- 直接内存是通过
ByteBuffer.allocateDirect()
方法来分配的,它是通过操作系统的本地方法直接分配内存的,不受 Java 堆大小的限制。 - 普通内存是通过 Java 虚拟机的堆内存来分配的,由 JVM 在堆内存中动态分配和回收内存,受到 Java 堆大小限制。
内存访问方式
- 直接内存的访问方式是直接访问,即不需要将数据从 本地内存复制到JVM 的堆内存,而是直接在本地内存中进行操作。
- 普通内存的访问方式是间接访问,即需要将数据从 JVM 的堆内存复制到 CPU 的缓存中才能进行操作,然后再将结果写回堆内存。
内存释放方式
- 直接内存的释放通常由操作系统负责,无需 JVM 进行垃圾回收操作。
- 普通内存的释放由 JVM 的垃圾回收器负责,需要等待垃圾回收器对不再使用的对象进行标记和回收操作
使用直接内存、非直接内存复制视频文件demo
最终直接内存比非直接内存速度快一倍。
package cn.itcast.protocol;
import cn.itcast.message.ChatRequestMessage;
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
/**
* @author Sherry Guo
* @date 2024/2/1 16:35
*/
public class Demo {
public static void main(String[] args) throws IOException {
String sourceFilePath = "E:\\BaiduNetdiskDownload\\bytebuffer.mp4";
String targetFilePath = "E:\\BaiduNetdiskDownload\\new.mp4";
String target2FilePath = "E:\\BaiduNetdiskDownload\\new2.mp4";
long start1 = System.currentTimeMillis();
// 使用直接内存复制视频文件
try (FileInputStream fis = new FileInputStream(sourceFilePath);
FileOutputStream fos = new FileOutputStream(targetFilePath);
FileChannel sourceChannel = fis.getChannel();
FileChannel targetChannel = fos.getChannel()) {
ByteBuffer buffer = ByteBuffer.allocateDirect(1024 * 1024); // 使用直接内存(1MB)
while (sourceChannel.read(buffer) != -1) {
buffer.flip();
targetChannel.write(buffer);
buffer.clear();
}
} catch (IOException e) {
e.printStackTrace();
}
long l = (System.currentTimeMillis() - start1);
System.out.println(l);
System.out.println("\n------------------------------------------");
long start2 = System.currentTimeMillis();
// 使用非直接内存复制视频文件
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream(sourceFilePath);
fos = new FileOutputStream(target2FilePath);
byte[] buffer = new byte[1024 * 1024]; // 使用非直接内存(1MB)
int bytesRead;
while ((bytesRead = fis.read(buffer)) != -1) {
fos.write(buffer, 0, bytesRead);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
fis.close();
fos.close();
}
long l2 = (System.currentTimeMillis() - start2) ;
System.out.println(l2);
}
}
直接内存案例中使用 try-with-resources 语句时,流会在代码块执行完毕后自动关闭,无需手动在 finally 块中关闭。这是 try-with-resources 语句的一大优点,可以避免手动关闭流可能导致的错误或遗漏。
释放直接内存
直接内存不受JVM管理,可使用Unsafe进行释放。