1、Java直接内存与堆内存-MarchOn
2、Java内存映射文件-MarchOn
3、Java Unsafe的使用-MarchOn
简单总结:
1、内存映射文件
读文件时候一般要两次复制:从磁盘复制到内核空间再复制到用户空间,内存映射文件避免了第二次复制,且内存分配在内核空间,应用程序访问的就是操作系统的内核内存空间,因此极大提高了读取效率。写文件同理。
2、堆内存分配与直接内存分配:
Java申请空间时通常是从JVM堆内存分配的,即 ByteBuffer.allocate(int ,但其实还可以直接从物理内存(用户空间内存?)分配,即 ByteBuffer.allocateDirect(int
通常来说,由于后者避免了数据在堆外内存和JVM堆内存间的复制,所以读写性能比前者的好,但是后者的分配比前者慢,特别是在数据量大的情况下差别更明显。此外,直接内存常被用来扩展可用的内存区域。
比较:
1 class DirectMemory {
2
3 // 分配堆内存
4 public static void bufferAccess() {
5 long startTime = System.currentTimeMillis();
6 ByteBuffer b = ByteBuffer.allocate(500);
7 for (int i = 0; i < 1000000; i++) {
8 for (int j = 0; j < 99; j++)
9 b.putInt(j);
10 b.flip();
11 for (int j = 0; j < 99; j++)
12 b.getInt();
13 b.clear();
14 }
15 long endTime = System.currentTimeMillis();
16 System.out.println("access_nondirect:" + (endTime - startTime));
17 }
18
19 // 直接分配内存
20 public static void directAccess() {
21 long startTime = System.currentTimeMillis();
22 ByteBuffer b = ByteBuffer.allocateDirect(500);
23 for (int i = 0; i < 1000000; i++) {
24 for (int j = 0; j < 99; j++)
25 b.putInt(j);
26 b.flip();
27 for (int j = 0; j < 99; j++)
28 b.getInt();
29 b.clear();
30 }
31 long endTime = System.currentTimeMillis();
32 System.out.println("access_direct:" + (endTime - startTime));
33 }
34
35 public static void bufferAllocate() {
36 long startTime = System.currentTimeMillis();
37 for (int i = 0; i < 1000000; i++) {
38 ByteBuffer.allocate(1000);
39 }
40 long endTime = System.currentTimeMillis();
41 System.out.println("allocate_nondirect:" + (endTime - startTime));
42 }
43
44 public static void directAllocate() {
45 long startTime = System.currentTimeMillis();
46 for (int i = 0; i < 1000000; i++) {
47 ByteBuffer.allocateDirect(1000);
48 }
49 long endTime = System.currentTimeMillis();
50 System.out.println("allocate_direct:" + (endTime - startTime));
51 }
52
53 public static void main(String args[]) {
54 System.out.println("访问性能测试:");
55 bufferAccess();
56 directAccess();
57
58 System.out.println();
59
60 System.out.println("分配性能测试:");
61 bufferAllocate();
62 directAllocate();
63 }
64 }
65
66 //结果
67
68 访问性能测试:
69 access_nondirect:160
70 access_direct:135
71
72 分配性能测试:
73 allocate_nondirect:231
74 allocate_direct:644
View Code
3、Unsafe类
直接内存分配(allocateDirect)其实就是调用了sun.misc.Unsafe类来进行内存分配,Unsafe是sun.*API中的类,它不是J2SE中真正的一部份。
关于JVM对内存分配、直接内存分配、内存映射文件的一个测试示例:
(2684862条记录,每条记录包含4个long值,所有记录以二进制形式存储在文件中)
以上述三种方式读取每条记录(每种方式都是一次就分配足够的内存,直接内存分配、JVM内存分配、内存映射文件三者分别用时35ms、46ms、22ms):
1 package buaa.act.ucar.imtg.main;
2
3 import java.io.IOException;
4 import java.io.RandomAccessFile;
5 import java.nio.ByteBuffer;
6 import java.nio.channels.FileChannel;
7 import java.nio.channels.FileChannel.MapMode;
8
9 /**
10 * @author zsm
11 * @date 2017年3月3日 上午10:23:53
12 */
13 public class Test {
14 public static void main(String[] args)
15 throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
16 long startTime, dataCount;
17
18 try {
19 startTime = System.currentTimeMillis();
20 System.out.println("reading");
21 dataCount = readFromMMFile("F:/gps data/2016-11-11 18087 60399647/beijing_0900-1500_2684862.binary");
22 System.out.printf("reading %d data,time used:%d ms \n", dataCount,
23 (System.currentTimeMillis() - startTime));
24 } catch (IOException e) {
25 // TODO Auto-generated catch block
26 e.printStackTrace();
27 }
28
29 }
30
31 public static long readFromFile(String srcFilePath) throws IOException {
32
33 RandomAccessFile randomAccessFileOutput = new RandomAccessFile(srcFilePath, "rw");
34 FileChannel inChannel = randomAccessFileOutput.getChannel();
35
36 long devsn, gpstime;
37 double longitude, latitude;
38 long dataCount = 0;
39
40 ByteBuffer byteBuffer = ByteBuffer.allocateDirect((int) randomAccessFileOutput.length());// 35ms
41 // ByteBuffer byteBuffer = ByteBuffer.allocate((int) randomAccessFileOutput.length());// 46ms
42 while (inChannel.read(byteBuffer) > 0) {
43 byteBuffer.flip();// 进入read模式
44 while (byteBuffer.hasRemaining()) {
45 devsn = byteBuffer.getLong();
46 gpstime = byteBuffer.getLong();
47 longitude = Double.longBitsToDouble(byteBuffer.getLong());
48 latitude = Double.longBitsToDouble(byteBuffer.getLong());
49 // System.out.println(devsn + " " + gpstime + " " + longitude + " " + latitude);
50 dataCount++;
51 }
52 byteBuffer.clear();// 进入write模式
53 }
54 inChannel.close();
55 randomAccessFileOutput.close();
56 return dataCount;
57 }
58
59 // 22ms
60 public static long readFromMMFile(String srcFilePath) throws IOException {
61 RandomAccessFile randomAccessFileOutput = new RandomAccessFile(srcFilePath, "rw");
62 FileChannel inChannel = randomAccessFileOutput.getChannel();
63
64 long devsn, gpstime;
65 double longitude, latitude;
66 long dataCount = 0;
67 ByteBuffer byteBuffer = inChannel.map(MapMode.READ_ONLY, 0, randomAccessFileOutput.length());
68 while (byteBuffer.hasRemaining()) {
69 devsn = byteBuffer.getLong();
70 gpstime = byteBuffer.getLong();
71 longitude = Double.longBitsToDouble(byteBuffer.getLong());
72 latitude = Double.longBitsToDouble(byteBuffer.getLong());
73 // System.out.println(devsn + " " + gpstime + " " + longitude + " " + latitude);
74 dataCount++;
75 }
76 inChannel.close();
77 randomAccessFileOutput.close();
78 return dataCount;
79 }
80
81 }
View Code