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:160
70 access_direct:135
71
72 分配性能测试:
73 allocate_nondirect:231
74 allocate_direct:644

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 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());// 45ms
41         // ByteBuffer byteBuffer = ByteBuffer.allocate((int) randomAccessFileOutput.length());// 46ms
42         // while (inChannel.read(byteBuffer) > 0) {// 加上wihle后,分别用时77ms,120ms
43         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     // 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 }

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

时间: 2024-12-10 12:53:09

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

java中内存分配策略及堆和栈的比较

2.1 内存分配策略 按照编译原理的观点,程序运行时的内存分配有三种策略,分别是静态的,栈式的,和堆式的. 静态存储分配是指在编译时就能确定每个数据目标在运行时刻的存储空间需求,因而在编译时就可以给他们分配固定的内存空间.这种分配策略要求程序代码中不允许有可变数据结构(比如可变数组)的存在,也不允许有嵌套或者递归的结构出现,因为它们都会导致编译程序无法计算准确的存储空间需求. 栈式存储分配也可称为动态存储分配,是由一个类似于堆栈的运行栈来实现的.和静态存储分配相反,在栈式存储方案中,程序对数据区

Cocos2d-x 3.x:如何进行合理的内存分配(使用AutoreleasePool 来合理的管理内存)

Cocos2d-x 3.x:如何进行合理的内存分配(使用AutoreleasePool 来合理的管理内存) 本文转载至深入理解Cocos2d-x 3.x:如何进行合理的内存分配 设想如下场景,这是一个典型的内存合理分配的场景:在一帧内,有若干个函数,每个函数都会创建一系列的精灵,每个精灵都不同,都会占用一定的内存,精灵的总数可能会有1000个,而一个函数只会创建10个精灵这样,创建的精灵只会在这个函数中使用,大致代码如下: 1 2 3 4 5 for(int i = 0; i < 10; i++

java 根据实体类创建映射文件

import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.lang.reflect.Field; import java.util.List; import java.util.Set; import com.mohe.model.entity.Teach; /** * 根据实体类生成映射文件 * * @author dxm * */ public class Obj

数据结构中的堆和栈 与 内存分配中的堆区和栈区 分析

比較全面的总结了诸多版本号,知识无国界.感谢各位的辛勤劳作. 在计算机领域,堆栈是一个不容忽视的概念,我们编写的C/C++语言程序基本上都要用到.但对于非常多的初学着来说,堆栈是一个非常模糊的概念. (1) 数据结构的栈和堆 首先在数据结构上要知道堆栈,虽然我们这么称呼它,但实际上堆栈是两种数据结构:堆和栈. 堆和栈都是一种数据项按序排列的数据结构. 栈就像装数据的桶或箱子 我们先从大家比較熟悉的栈说起吧.它是一种具有后进先出性质的数据结构,也就是说后存放的先取.先存放的后取.这就如同我们要取出

关于c语言内存分配,malloc,free,和段错误,内存泄露

1.   C语言的函数malloc和free (1) 函数malloc和free在头文件<stdlib.h>中的原型及参数        void * malloc(size_t size) 动态配置内存,大小有size决定,返回值成功时为任意类型指针,失败时为NULL. void  free(void *ptr) 释放动态申请的内存空间,调用free()后ptr所指向的内存空间被收回,如果ptr指向未知地方或者指向的空间已被收回,则会发生不可预知的错误,如果ptr为NULL,free不会有任

内存分配中的堆、栈、静态区、只读区

内存中的栈区处于相对较高的地址以地址的增长方向为上的话,栈地址是向下增长的,栈中分配局部变量空间,堆区是向上增长的用于分配程序员申请的内存空间.另外还有静态区是分配静态变量,全局变量空间的:只读区是分配常量和程序代码空间的:以及其他一些分区.对于常量,在实际情况中,是会复用的,比如变量a和b都赋值为”abc”则实际上他们指向同一块地址. main.cpp int a = 0; //全局初始化区 char *p1; //全局未初始化区 main() { int b; //栈 char s[] =

C++程序内存分配方式(堆与栈)

一.内存布局 1.栈区(stack):由编译器自动分配释放,存放函数的参数值,局部变量值等,其操作方法类似数据结构中的栈. 2.堆区(heap):一般由程序员分配释放,与数据结构中的堆毫无关系,分配方式类似于链表. 3.全局/静态区(static):全局变量和静态变量的存储是放在一起的,在程序编译时分配. 4.文字常量区:存放常量字符串. 5.程序代码区:存放函数体(类的成员函数.全局函数)的二进制代码 二.栈与堆的比较 1.申请方式 stack:系统自动分配,如声明int a;系统自动在栈空间

Java内存分配之堆、栈和常量池(转)

摘录自http://www.cnblogs.com/SaraMoring/p/5687466.html Java内存分配主要包括以下几个区域: 1. 寄存器:我们在程序中无法控制 2. 栈:存放基本类型的数据和对象的引用,但对象本身不存放在栈中,而是存放在堆中 3. 堆:存放用new产生的数据 4. 静态域:存放在对象中用static定义的静态成员 5. 常量池:存放常量 6. 非RAM(随机存取存储器)存储:硬盘等永久存储空间 *********************************

Java中堆内存与栈内存分配浅析

Java把内存划分成两种:一种是栈内存,另一种是堆内存.在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配,当在一段代码块定义一个变量时,Java就在栈中为这个变量分配内存空间,当超过变量的作用域后,Java 会自动释放掉为该变量分配的内存空间,该内存空间可以立即被另作它用. 堆内存用来存放由 new 创建的对象和数组,在堆中分配的内存,由 Java 虚拟机的自动垃圾回收器来管理.在堆中产生了一个数组或者对象之后,还可以在栈中定义一个特殊的变量,让栈中的这个变量的取值等于数

JAVA中堆栈和内存分配

(一).栈.堆 1.寄存器:最快的存储区, 由编译器根据需求进行分配,我们在程序中无法控制.2. 栈:存放基本类型的变量数据和对象的引用,但对象本身不存放在栈中,而是存放在堆(new 出来的对象)或者常量池中(字符串常量对象存放在常量池中.)3. 堆:存放所有new出来的对象.4. 静态域(属于方法区) :存放静态成员(static定义的)5. 常量池 (属于方法区):存放字符串常量和基本类型常量(public static final).6. 非RAM存储:硬盘等永久存储空间这里我们主要关心栈