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

时间:2023-03-09 01:25:40
目录_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类进行分配(见下节)。后者的分配原理是这样的:使用Native函数库直接分配堆外内存,通过一个存储在java堆中的DirectByteBuffer对象作为这块内存的引用进行操作,从而避免了在java堆和Native堆之间复制数据的开销。

通常来说,由于后者避免了数据在堆外内存和JVM堆内存间的复制,所以读写性能比前者的好,但是后者的分配比前者慢,特别是在数据量大的情况下差别更明显。此外,直接内存常被用来扩展可用的内存区域。

比较:

 class DirectMemory {

     // 分配堆内存
public static void bufferAccess() {
long startTime = System.currentTimeMillis();
ByteBuffer b = ByteBuffer.allocate(500);
for (int i = 0; i < 1000000; i++) {
for (int j = 0; j < 99; j++)
b.putInt(j);
b.flip();
for (int j = 0; j < 99; j++)
b.getInt();
b.clear();
}
long endTime = System.currentTimeMillis();
System.out.println("access_nondirect:" + (endTime - startTime));
} // 直接分配内存
public static void directAccess() {
long startTime = System.currentTimeMillis();
ByteBuffer b = ByteBuffer.allocateDirect(500);
for (int i = 0; i < 1000000; i++) {
for (int j = 0; j < 99; j++)
b.putInt(j);
b.flip();
for (int j = 0; j < 99; j++)
b.getInt();
b.clear();
}
long endTime = System.currentTimeMillis();
System.out.println("access_direct:" + (endTime - startTime));
} public static void bufferAllocate() {
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
ByteBuffer.allocate(1000);
}
long endTime = System.currentTimeMillis();
System.out.println("allocate_nondirect:" + (endTime - startTime));
} public static void directAllocate() {
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
ByteBuffer.allocateDirect(1000);
}
long endTime = System.currentTimeMillis();
System.out.println("allocate_direct:" + (endTime - startTime));
} public static void main(String args[]) {
System.out.println("访问性能测试:");
bufferAccess();
directAccess(); System.out.println(); System.out.println("分配性能测试:");
bufferAllocate();
directAllocate();
}
} //结果 访问性能测试:
access_nondirect:160
access_direct:135 分配性能测试:
allocate_nondirect:231
allocate_direct:644

3、Unsafe类

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

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

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

以上述三种方式读取每条记录(每种方式都是一次就分配足够的内存,直接内存分配、JVM内存分配、内存映射文件三者分别用时35ms、46ms、22ms):

 package buaa.act.ucar.imtg.main;

 import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode; /**
* @author zsm
* @date 2017年3月3日 上午10:23:53
*/
public class Test {
public static void main(String[] args)
throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
long startTime, dataCount; try {
startTime = System.currentTimeMillis();
System.out.println("reading");
dataCount = readFromMMFile("F:/gps data/2016-11-11 18087 60399647/beijing_0900-1500_2684862.binary");
System.out.printf("reading %d data,time used:%d ms \n", dataCount,
(System.currentTimeMillis() - startTime));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} } public static long readFromFile(String srcFilePath) throws IOException { RandomAccessFile randomAccessFileOutput = new RandomAccessFile(srcFilePath, "rw");
FileChannel inChannel = randomAccessFileOutput.getChannel(); long devsn, gpstime;
double longitude, latitude;
long dataCount = 0; ByteBuffer byteBuffer = ByteBuffer.allocateDirect((int) randomAccessFileOutput.length());// 35ms
// ByteBuffer byteBuffer = ByteBuffer.allocate((int) randomAccessFileOutput.length());// 46ms
while (inChannel.read(byteBuffer) > 0) {
byteBuffer.flip();// 进入read模式
while (byteBuffer.hasRemaining()) {
devsn = byteBuffer.getLong();
gpstime = byteBuffer.getLong();
longitude = Double.longBitsToDouble(byteBuffer.getLong());
latitude = Double.longBitsToDouble(byteBuffer.getLong());
// System.out.println(devsn + " " + gpstime + " " + longitude + " " + latitude);
dataCount++;
}
byteBuffer.clear();// 进入write模式
}
inChannel.close();
randomAccessFileOutput.close();
return dataCount;
} // 22ms
public static long readFromMMFile(String srcFilePath) throws IOException {
RandomAccessFile randomAccessFileOutput = new RandomAccessFile(srcFilePath, "rw");
FileChannel inChannel = randomAccessFileOutput.getChannel(); long devsn, gpstime;
double longitude, latitude;
long dataCount = 0;
ByteBuffer byteBuffer = inChannel.map(MapMode.READ_ONLY, 0, randomAccessFileOutput.length());
while (byteBuffer.hasRemaining()) {
devsn = byteBuffer.getLong();
gpstime = byteBuffer.getLong();
longitude = Double.longBitsToDouble(byteBuffer.getLong());
latitude = Double.longBitsToDouble(byteBuffer.getLong());
// System.out.println(devsn + " " + gpstime + " " + longitude + " " + latitude);
dataCount++;
}
inChannel.close();
randomAccessFileOutput.close();
return dataCount;
} }