Java共享内存

1   共享内存对应应用开发的意义

对熟知UNIX系统应用开发的程序员来说,IPC(InterProcess  
Communication)机制是非常熟悉的,IPC基本包括共享内存、信号灯操作、消息队列、信号处理等部分,是开发应用中非常重要的必不可少的工具。其中共享内存IPC机制的关键,对于数据共享、系统快速查询、动态配置、减少资源耗费等均有独到的优点。

对应UNIX系统来说,共享内存分为一般共享内存和映像文件共享内存两种,而对应   Windows,实际上只有映像文件共享内存一种。所以java应用中也是只能创建映像文件共享内存。

在java语言中,基本上没有提及共享内存这个概念,但是,在某一些应用中,共享内存确实非常有用,例如采用java语言的分布式应用系统中,存在着大量的分布式共享对象,很多时候需要查询这些对象的状态,以查看系统是否运行正常或者了解这些对象的目前的一些统计数据和状态。如果采用网络通信的方式,显然会增加应用的额外负担,也增加了一些不必要的应用编程。而如果采用共享内存的方式,则可以直接通过共享内存查看对象的状态数据和统计数据,从而减少了一些不必要的麻烦。

共享内存的使用有如下几个特点:

可以被多个进程打开访问;

读写操作的进程在执行读写操作时其他进程不能进行写操作;

多个进程可以交替对某一共享内存执行写操作;

一个进程执行了内存的写操作后,不影响其他进程对该内存的访问。同时其他进程对更新后的内存具有可见性。

在进程执行写操作时如果异常退出,对其他进程写操作禁止应自动解除。

相对共享文件,数据访问的方便性和效率有

另外,共享内存的使用上有如下情况:

独占的写操作,相应有独占的写操作等待队列。独占的写操作本身不会发生数据的一致性问题。

共享的写操作,相应有共享的写操作等待队列。共享的写操作则要注意防止发生数据的一致性问题。

独占的读操作,相应有共享的读操作等待队列;

共享的读操作,相应有共享的读操作等待队列。

一般情况下,我们只是关心第一二种情况。

2   共享内存在java中的实现

在jdk1.4中提供的类MappedByteBuffer为我们实现共享内存提供了较好的方法。该缓冲区实际上是一个磁盘文件的内存映像。二者的变化将保持同步,即内存数据发生变化会立刻反映到磁盘文件中,这样会有效的保证共享内存的实现。

将共享内存和磁盘文件建立联系的是文件通道类:FileChannel。该类的加入是JDK为了统一对外部设备(文件、网络接口等)的访问方法,并且加强了多线程对同一文件进行存取的安全性。例如读写操作统一成read和write。这里只是用它来建立共享内存用,它建立了共享内存和磁盘文件之间的一个通道。

打开一个文件建立一个文件通道可以用RandomAccessFile类中的方法getChannel。该方法将直接返回一个文件通道。该文件通道由于对应的文件设为随机存取文件,一方面可以进行读写两种操作,另一方面使用它不会破坏映像文件的内容(如果用FileOutputStream直接打开一个映像文件会将该文件的大小置为0,当然数据会全部丢失)。这里,如果用
 
FileOutputStream和FileInputStream则不能理想的实现共享内存的要求,因为这两个类同时实现自由的读写操作要困难得多。

下面的代码实现了如上功能,它的作用类似UNIX系统中的mmap函数。

  //   获得一个只读的随机存取文件对象
  RandomAccessFile   RAFile   =   new   RandomAccessFile(filename,"r");  

  //   获得相应的文件通道
  FileChannel   fc   =   RAFile.getChannel();  

  //   取得文件的实际大小,以便映像到共享内存
  int   size   =   (int)fc.size();  

  //   获得共享内存缓冲区,该共享内存只读
  MappedByteBuffer   mapBuf   =   fc.map(FileChannel.MAP_RO,0,size);  

  //   获得一个可读写的随机存取文件对象
  RAFile   =   new   RandomAccessFile(filename,"rw");  

  //   获得相应的文件通道
  fc   =   RAFile.getChannel();  

  //   取得文件的实际大小,以便映像到共享内存
  size   =   (int)fc.size();  

  //   获得共享内存缓冲区,该共享内存可读写
  mapBuf   =   fc.map(FileChannel.MAP_RW,0,size);  

  //   获取头部消息:存取权限
  mode   =   mapBuf.getInt();

如果多个应用映像同一文件名的共享内存,则意味着这多个应用共享了同一内存数据。这些应用对于文件可以具有同等存取权限,一个应用对数据的刷新会更新到多个应用中。

为了防止多个应用同时对共享内存进行写操作,可以在该共享内存的头部信息加入写操作标志。该共享内存的头部基本信息至少有:

  int   Length;   //   共享内存的长度。
  int   mode;   //   该共享内存目前的存取模式。

共享内存的头部信息是类的私有信息,在多个应用可以对同一共享内存执行写操作时,开始执行写操作和结束写操作时,需调用如下方法:

public   boolean   StartWrite()
  {
  if(mode   ==   0)   {   //   标志为0,则表示可写
  mode   =   1;   //   置标志为1,意味着别的应用不可写该共享内存
  mapBuf.flip();
  mapBuf.putInt(mode);   //   写如共享内存的头部信息
  return   true;
  }
  else   {
  return   false;   //   指明已经有应用在写该共享内存,本应用不可写该共享内存
  }
  }  

  public   boolean   StopWrite()
  {
  mode   =   0;   //   释放写权限
  mapBuf.flip();
  mapBuf.putInt(mode);   //   写入共享内存头部信息
  return   true;
  }

这里提供的类文件mmap.java封装了共享内存的基本接口,读者可以用该类扩展成自己需要的功能全面的类。

如果执行写操作的应用异常中止,那么映像文件的共享内存将不再能执行写操作。为了在应用异常中止后,写操作禁止标志自动消除,必须让运行的应用获知退出的应用。在多线程应用中,可以用同步方法获得这样的效果,但是在多进程中,同步是不起作用的。方法可以采用的多种技巧,这里只是描述一可能的实现:采用文件锁的方式。写共享内存应用在获得对一个共享内存写权限的时候,除了判断头部信息的写权限标志外,还要判断一个临时的锁文件是否可以得到,如果可以得到,则即使头部信息的写权限标志为1(上述),也可以启动写权限,其实这已经表明写权限获得的应用已经异常退出,这段代码如下:

  //   打开一个临时的文件,注意同一共享内存,该文件名要相同,可以在共享文件名后加后缀“.lock”。
  RandomAccessFile   fis   =   new   RandomAccessFile("shm.lock","rw");
  //   获得文件通道
  FileChannel   lockfc   =   fis.getChannel();
  //   获得文件的独占锁,该方法不产生堵塞,立刻返回
  FileLock   flock   =   lockfc.tryLock();
  //   如果为空,则表明已经有应用占有该锁
  if(flock   ==   null)   {
  ...//   不能执行写操作
  }
  else   {
  ...//   可以执行写操作
  }

该锁会在应用异常退出后自动释放,这正是该处所需要的方法。

3   共享内存在java中的应用

共享内存在java应用中,经常有如下两种种应用:

永久对象配置。

在java服务器应用中,用户可能会在运行过程中配置一些参数,而这些参数需要永久有效,当服务器应用重新启动后,这些配置参数仍然可以对应用起作用。这就可以用到该文中的共享内存。该共享内存中保存了服务器的运行参数和一些对象运行特性。可以在应用启动时读入以启用以前配置的参数。

查询共享数据。

一个应用(例   sys.java)是系统的服务进程,其系统的运行状态记录在共享内存中,其中运行状态可能是不断变化的。为了随时了解系统的运行状态,启动另一个应用(例   mon.java),该应用查询该共享内存,汇报系统的运行状态。

可见,共享内存在java应用中还是很有用的,只要组织好共享内存的数据结构,共享内存就可以在应用开发中发挥很不错的作用。

可以实际测试一下下面的程式处理:

      public   QuickFind()   {
      .....
      .....
              FileChannel   fch   =   new   RandomAccessFile(path,   "r").getChannel();
              ByteBuffer   roBuf   =   fch.map(FileChannel.MapMode.READ_ONLY,   0,   (int)fch.size());
              DataInputStream   is   =   new   DataInputStream(newInputStream(roBuf));
              String   aa   =   "";
              while(true){
                  aa   =   is.readLine();
            .....
              .....
      }  

      public   static   InputStream   newInputStream(final   ByteBuffer   buf)   {
              return   new   InputStream()   {
                      public   synchronized   int   read()   throws   IOException   {
                              if   (!buf.hasRemaining())   {
                                      return   -1;
                              }
                              return   buf.get();
                      }  

                      public   synchronized   int   read(byte[]   bytes,   int   off,   int   len)   throws   IOException   {
                              //   Read   only   what‘s   left
                              len   =   Math.min(len,   buf.remaining());
                              buf.get(bytes,   off,   len);
                              return   len;
                      }
              };
      }

速度应该可以提升许多

时间: 2024-10-06 11:32:17

Java共享内存的相关文章

Android系统匿名共享内存(Anonymous Shared Memory)Java调用接口分析

一.Ashmem驱动程序 ~/Android/kernel/goldfish ----include ----linux ----ashmem.h ----mm ----ashmem.c 驱动程序具体解释请看<Android系统源码情景分析>.作者罗升阳. 二.执行时库cutils的匿名共享内存訪问接口 ~/Android/system/core ----libcutils ----ashmem-dev.c 具体解释请看<Android系统源码情景分析>,作者罗升阳. 三.Memo

java虚拟机---内存

java虚拟机---内存 Java虚拟机,即JVM,负责运行java程序,每个java程序都运行在一个具体jvm实例上.Java虚拟机的体系架构分为:类装载子系统.运行时数据区.执行引擎.类装载子系统即负责加载.验证.解析.初始化java类的系统:Java虚拟机在运行一个程序时需要储存很多数据,如类装载信息.创建的实例对象.方法调用的参数.局部变量.中间值等,虚拟机把这些信息都储存在"运行时数据区"里,即这里讲的JVM内存:执行引擎则是以字节码形式的class文件为输入,运行程序输出计

Java虚拟机内存分配详解

简介 了解Java虚拟机内存分布的好处 1.了解Java内存管理的细节,有助于程序员编写出性能更好的程序.比如,在新的线程创建时,JVM会为每个线程创建一个专属的栈 (stack),其栈是先进后出的数据结构,这种方式的特点,让程序员编程时,必须特别注意递归方法要尽量少使用,另外栈的大小也有一定的限制,如果过多 的递归,容易导致stack overflow. 2.了解Java内存管理的细节,一旦内存管理出现问题,有助于找到问题的根本原因所在. 3.了解Java内存管理的内幕,有助于优化JVM,从而

Java静态内存与动态内存分配的解析

1. 静态内存 静态内存是指在程序开始运行时由编译器分配的内存,它的分配是在程序开始编译时完成的,不占用CPU资源. 程序中的各种变量,在编译时系统已经为其分配了所需的内存空间,当该变量在作用域内使用完毕时,系统会 自动释放所占用的内存空间. 变量的分配与释放,都无须程序员自行考虑. eg:基本类型,数组 2. 动态内存 用户无法确定空间大小,或者空间太大,栈上无法分配时,会采用动态内存分配. 3. 区别 a) 静态内存分配在编译时完成,不占用CPU资源; 动态内存分配在运行时,分配与释放都占用

Fresco内存机制(Ashmem匿名共享内存)

Fresco的内存机制 Fresco是Facebook出品的高性能图片加载库,采用了Ashmem匿名共享内存机制, 来解决图片加载中的OOM问题.这里不对Fresco做深入分析,只关注Fresco在Android Bitmap的管理上采用了哪些黑科技. Android的内存区域 Java Heap(Dalvik Heap),这部分的内存区域是由Dalvik虚拟机管理,通过Java中 new 关键字来申请一块新内存.这块区域的内存是由GC直接管理,能够自动回收内存.这块内存的大小会受到系统限制,当

管道,信号量,共享内存,socket的实际使用场景和NSPipe管道的使用

找了很久也没有找到NSPipe在IOS方面的常规使用().我试了半天终于找到它的正常的使用方法,我想对很多想使用管道会有很大的帮助.阿门,看来我是第一个吃螃蟹的人. 进程和线程间四大通信机制:管道,信号量,共享内存,socket. 四大通信机制的实际使用场景 管道是单向的.先进先出的,它把一个进程的输出和另一个进程的输入连接在一起.一个进程(写进程)在管道的尾部写入数据,另一个进程(读进程)从管道的头部读出数据.NSPipe包含两个文件描述符,读文件描述符合写文件描述.管道通常用在两个线程间通信

并发模型—共享内存模型(线程与锁)示例篇

共享内存模型,顾名思义就是通过共享内存来实现并发的模型,当多个线程在并发执行中使用共享资源时如不对所共享的资源进行约定或特殊处理时就会出现读到脏数据.无效数据等问题:而为了决解共享资源所引起的这些问题,Java中引入了同步.锁.原子类型等这些用于处理共享资源的操作; 在本篇文章中,将通过几个Demo来介绍Java的synchronized.lock.atomic相关类等,Java的共享内存并发模型也就体现在同步(synchronized).锁(lock)等这些实现上: 同步: Demo中开启两个

Java虚拟机内存管理机制

自动内存管理机制 Java虚拟机(JVM)在执行Java程序过程中会把它所管理的内存划分为若干个不同的数据区域.这些区域都有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而存在,有的区域则是依赖用户线程的启动和结束而建立和销毁.根据<Java虚拟机规范 第2版>规定,运行时数据区包括: 1.程序计数器 一块较小的内存空间,不在Ram上,而是直接划分在CPU上的,程序员无法直接操作它.当前线程所执行的字节码的行号指示器,通过改变这个计数器的值来选取下一条需要执行的字节码指令.每条

Java之内存分析和String对象

http://www.cnblogs.com/devinzhang/archive/2012/01/25/2329463.html Java中内存分析: 栈(Stack) :存放基本类型的变量数据和对象的引用,但对象本身不存放在栈中,而是存放在堆(new 出来的对象)或者常量池中(字符串常量对象存放在常量池中). 堆(heap):存放所有new出来的对象. 常量池(constant pool):在堆中分配出来的一块存储区域,存放储显式的String常量和基本类型常量(float.int等).另外