首页 > 代码库 > Java内存分配(直接内存、堆内存、Unsafel类、内存映射文件)

Java内存分配(直接内存、堆内存、Unsafel类、内存映射文件)

1、Java直接内存与堆内存-MarchOn

2、Java内存映射文件-MarchOn

3、Java Unsafe的使用-MarchOn

 

简单总结:

1、内存映射文件

读文件时候一般要两次复制:从磁盘复制到内核空间再复制到用户空间,内存映射文件避免了第二次复制,且内存分配在内核空间,应用程序访问的就是操作系统的内核内存空间,因此极大提高了读取效率。写文件同理。

2、堆内存分配与直接内存分配:

Java申请空间时通常是从JVM堆内存分配的,即 ByteBuffer.allocate(int capacity) ,但其实还可以直接从物理内存(用户空间内存?)分配,即 ByteBuffer.allocateDirect(int capacity) ,后者其实调用了Unsafe类进行分配(见下节)。通常来说,后者的读写性能比前者的好,但是后者的分配比前者慢,特别是在数据量大的情况下差别更明显。

比较:

技术分享
 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:16070 access_direct:13571 72 分配性能测试:73 allocate_nondirect:23174 allocate_direct:644
View Code

3、Unsafe类

直接内存分配(allocateDirect)其实就是调用了sun.misc.Unsafe类来进行内存分配,Unsafe是sun.*API中的类,它不是J2SE中真正的一部份。

 

关于JVM对内存分配、直接内存分配、内存映射文件的一个测试示例:

2684862条记录,每条记录包含4个long值,所有记录以二进制形式存储在文件中

以上述三种方式读取每条记录(每种方式都是一次就分配足够的内存):

技术分享
 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 zsm11  * @date 2017年3月3日 上午10:23:5312  */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 block26             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());// 45ms41         // ByteBuffer byteBuffer = ByteBuffer.allocate((int) randomAccessFileOutput.length());// 46ms42         // while (inChannel.read(byteBuffer) > 0) {// 加上wihle后,分别用时77ms,120ms43         byteBuffer.rewind();// 进入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     // 22ms60     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

 

前两者要45ms左右,而内存映射文件只要22ms左右。

 

Java内存分配(直接内存、堆内存、Unsafel类、内存映射文件)