技术开发 频道

如何提高系统性能指标

  解决方案--我们想到了什么?

  明确了目标之后,我们就开始了在Java中寻找的征程。可以说几乎涵盖了现有的所有可行的方案,下面就是我们探索和思考的点点滴滴。

  一、 利用JNI

  我们首先想到的就是JNI(Java Native Interface,Java本地接口),毕竟这是最直观和最省事的解决方案。在Java中利用JNI直接调用已有的VC或者VB代码,不需要重新编写这些代码,节省了时间,而且程序执行效率也相当不错。但是,利用JNI也存在着诸多的问题:不同程序代码之间的兼容性和可协调性,不易维护性。总之,对于这种夹生饭可以作为一时的权宜之计,在项目时间紧迫的情况下可以考虑使用,但是从长远考虑还是不宜采用。(3)

  二、 利用XML

  这其中我们也想到了利用XML,作为时下非常流行和实用的一门技术,Jdk1.4中提供了一整套比较完整的XML API,使得产生以及解析XML文件变得非常的容易。但是,个人觉得XML最大的优势在于为不同系统间的数据交换提供一种通用的格式,在于数据存储、解析和转换方面,作为数据缓存的候选虽然也未尝不可,但是从最优系统性能和充分继承原有系统架构考虑,还不是非常受欢迎的解决方案。

  三、 利用MMF

  因为原有系统是使用的MMF,所以我们也自然而然想到了JAVA中是否也存在MMF。经过对Jdk1.4的仔细研究,我们也如愿找到了我们希望的功能。经过各方面的讨论,我们决定在新系统中采用该技术。

  解决方案--我们做了些什么?

  在做出决定之后,我们就需要对Java中的MMF做一个详细的研究。在Jdk1.4中,关于MMF的API主要位于java.nio和java.nio.channels包下。在新的JAVA NIO中着重提到两个概念Buffer和Channel,MMF其实是作为它们的一个附属品被提出来的。其中的FileChannel类的map方法能够完成这样一种功能"Maps a region of this channel's file directly into memory",返回一个MappedByteBuffer对象。由此我们可见在Jdk1.4中,MMF的表现形式为MappedByteBuffer类及其父类ByteBuffer,你可以通过这些类提供的一些方法来操纵MMF对象,而创建MMF的功能主要由FileChannel类来完成。(4)

  在使用类MappedByteBuffer之前,你必须弄清楚这样几个概念:capacity, limit, position,这在所有Buffer类中都是非常关键的。这里我直接引用Jdk1.4文档中的解释:

  A buffer's capacity is the number of elements it contains. The capacity of a buffer is never negative and never changes.

  A buffer's limit is the index of the first element that should not be read or written. A buffer's limit is never negative and is never greater than its capacity.

  A buffer's position is the index of the next element to be read or written. A buffer's position is never negative and is never greater than its limit.

  也许这样一个数学公式更加直观:0 <= position <= limit <= capacity。

  在进行大规模的系统应用之前,我们建立个简单的应用模型。今天,我们介绍一下这其中关于MMF最简单的一些操作。

  1、 创建MMF

  上面我们已经提到,调用FileChannel类的map()方法可以创建MMF,详细的方法说明如下:

1 abstract MappedByteBuffer map(FileChannel.MapMode mode, long position, long size)
2

  通过设置不同的MapMode类型,可以分别得到只读的、可读写的和私有的MMF,因此可以视情况而定创建不同的MMF。同时通过设置参数position和size可以指定文件的某一部分映射至内存,该特点对于大文件是非常有用的。

1 //清单一:创建不同类型的MMF
2 try
3 {
4         File file = new File("filename");
5     
6         // 创建一个只读的memory-mapped file
7         FileChannel roChannel = new RandomAccessFile(file, "r").getChannel();
8         ByteBuffer roBuf = roChannel.map(FileChannel.MapMode.READ_ONLY, 0, (int)roChannel.size());
9     
10         // 创建一个可读写的 memory-mapped file
11         FileChannel rwChannel = new RandomAccessFile(file, "rw").getChannel();
12         ByteBuffer wrBuf = rwChannel.map(FileChannel.MapMode.READ_WRITE, 0, (int)rwChannel.size());
13     
14         // 创建一个私有的 (copy-on-write) memory-mapped file.
15         // Any write to this channel results in a private copy of the data.
16         FileChannel pvChannel = new RandomAccessFile(file, "rw").getChannel();
17         ByteBuffer pvBuf = roChannel.map(FileChannel.MapMode.PRIVATE, 0, (int)rwChannel.size());
18 }
19 catch (IOException e)
20 {}
21

  2、 向MMF中插入数据

  你可以利用类MappedByteBuffer的capacity来得到它里面包含的字节数,这是个常量。你可以利用方法put()来向MMF中插入数据,它有两种不同的版本:绝对位置插入put(int index, byte b),为此你必须指定index(0<=index<=capacity-1);相对位置插入put(byte b),它是利用了position和limit属性。利用相对位置插入数值后,position也相应地加1,直至达到limit的限制。而且,针对不同的数据类型,有各自相对应的put方法,比如putChar, putDouble之类。

1 // Create an empty MappedByteBuffer with a 10 byte capacity
2     ByteBuffer bbuf = MappedByteBuffer.allocate(10);
3     
4     // Get the buffer's capacity
5     int capacity = bbuf.capacity(); // 10
6     
7     // Use the absolute put().
8     // This method does not affect the position.
9     bbuf.put(1,(byte)0xFF); // position=0
10     
11     // Set the position
12     bbuf.position(5);
13     
14     // Use the relative put()
15     bbuf.put((byte)0xFF);
16     
17     // Get the new position
18     int pos = bbuf.position(); // 6
19     
20     // Get remaining byte count
21     int rem = bbuf.remaining(); // 4
22     
23     // Set the limit
24     bbuf.limit(7); // remaining=1
25     
26     // This convenience method sets the position to 0
27     bbuf.rewind(); // remaining=7
28

  3、 从MMF中获得数据

  与上述的过程相反,你可以通过不同的get方法来从MMF中获得数据。

1 // Create an empty MappedByteBuffer with a 10 byte capacity
2     ByteBuffer bbuf = MappedByteBuffer.allocate(10);
3     
4     // Get the MappedByteBuffer's capacity
5     int capacity = bbuf.capacity(); // 10
6     
7     // Use the absolute get().
8     // This method does not affect the position.
9     byte b = bbuf.get(5); // position=0
10     
11     // Set the position
12     bbuf.position(5);
13     
14     // Use the relative get()
15     b = bbuf.get();
16     
17     // Get the new position
18     int pos = bbuf.position(); // 6
19     
20     // Get remaining byte count
21     int rem = bbuf.remaining(); // 4
22     
23     // Set the limit
24     bbuf.limit(7); // remaining=1
25     
26     // This convenience method sets the position to 0
27     bbuf.rewind(); // remaining=7
28

  解决方案--需要注意的地方

  上面我们给出的只是一个非常简单的读写MMF的例子,在实际的使用过程中会复杂得多,下面几个因素可能是你要好好考虑的:

  1、 数据与MMF的对应关系

  既然是要将数据缓存到MMF中,那我们就必须确立数据库表与MMF的对应关系。我们推荐使用的方式是每一张表对应一个MMF文件。

  2、 MMF文件长度的设计

  确立了对应关系之后,我们需要分析一下如何设定MMF文件的初始长度。文件长度不能太小,否则就不能容纳所有的数据,同时文件也不能太长,那样一来浪费系统内存,二来也会使创建MMF的开销急剧增大。那刚好能容纳所有的记录呢?听起来是个不错的主意,但是如果这个时候需要添加一条记录呢?麻烦就来啦,由于原有长度不够。系统需要重新re-map MMF文件,造成系统内频繁地创建MMF,反而使性能下降。经过我们研究后得出,这个比例在1.1-1.3之间比较合适,也就是MMF文件略大于表中现有记录的总和。

  3、 针对不同性质的数据进行不同的处理

  明确以上两点,我们还需要对数据本身做一番研究。有些数据趋于固化,一般不会有什么改变,比如国家、省份等,而有些数据则会经常变化,比如产品等,对于这两种不同类型的数据,你可以采取不同的处理方式,以达到最优的系统性能。

  可能存在的问题--我们需要预防些什么?

  1、 MMF不是功能较多灵药

  千万不要以为有了MMF,你就可以高枕无忧,可以轻轻松松搞定系统的缓存机制。事实远非如此,MMF只不过是一把利刃,更重要的是你自己要仔细认真地设计好系统的缓存机制。要知道,解决交通堵塞问题的关键不是把路修得多么宽,而是要合理地规划整个交通路线。要知道在某些操作系统中,使用MMF的代价是非常昂贵的,失去好的规划,你可能会适得其反,系统反而会更加的拥挤不堪。况且,使用MMF还会带来很多的副作用。

  2、 性能与数据差错容忍度之间的平衡

  我们知道,随着数据缓存的大量使用,不可避免地会产生某种程度上的数据不一致,也就可能会产生某些数据差错。所以说,数据缓存使用的力度决定于系统客户对这些错误的容忍程度有多大。在某些非常关键的业务数据应用数据缓存技术时,必须格外地小心。

  3、 需要额外的MMF支持代码

  如上所述,为了最大限度地减少数据的不统一,我们必须提供一套非常合理和有效的数据同步机制,某种程度上甚至可以认为数据同步机制的好坏决定了数据缓存技术的成败。而这些是我们在使用MMF的过程中需要额外提供的代码。

  4、 MMF与平台的相关性

  现在大部分编程语言中使用MMF的方法都是,提供相应的接口创建和操作MMF或者系统API,而底层的具体MMF细节则由相应的操作系统去决定。这样每种操作系统中MMF不同的实现细节也在某种程度上影响着我们对MMF的使用。

  5、 使用MMF必须十分的小心

  既然MMF是贮存在系统内存中,所以对于某些错误必须时刻警惕,比如"Array Out of Bound"等。要是您的系统没有很好地捕获这些错误,您的系统可能会彻底崩溃。每当你编写这些MMF代码的时候,你必须时刻牢记在心:我是在与系统内存打交道,这家伙可是娇贵的很。

  6、 由于Jdkl1.4的推出时间不长,基于MMF的现有应用几乎没有,所以没有真正能够在现实环境中检验MMF的使用情况,可能会存在一些不可预知的风险。

  总结

  通过以上的介绍,相信大家对MMF在Java中的应用都有了一个初步的印象。实际上,提高应用系统的性能一直是所有应用系统开发人员追求的目标。除去本文谈到的缓存技术之外,在J2EE中,你还可以通过各种池技术的应用,EJB组件的优化来提高系统性能

0
相关文章