ByteBuffer使用和实现以及文件内存映射

由于工作等时间安排的原因,这篇文章被积蓄了很久。也好,一些内容也得到了沉淀。上一篇Java NIO的文章已经对Buffer和各个子类做了最基本的介绍,但在Java NIO中为了提高效率实际上是更接近了系统底层的实现方式的,比如在Channel的使用中,实际上在很多方法里使用的Buffer是针对字节的ByteBuffer,在与操作系统协同工作时,使用字节交换数据省去了许多麻烦。本篇文章就对ByteBuffer做进一步介绍,包括字节顺序、ByteBuffer的实现、ByteBuffer与其它Buffer直接子类的关系和内存映射相关Buffer使用等内容的整理。除此之外,也会提下JDK中的实现类。

0. ByteBuffer和字节序

ByteBuffer和CharBuffer等其它Buffer的直接子类一样,顾名思义,就是存取字节的Buffer。很多数据最终在和底层交互上都是使用了字节,而更大的数据是由字节组合而成。谈到字节的组合,就不得不谈到字节大小的定义和字节的顺序。关于字节是8位构成的这个结论,似乎现在的计算机教材都理所当然地描述出来,我们也默认接受了这样的一个事实。但实际上字节由8个二进制位构成也是有渊源和优点的,这与IBM的360主机有关,详细的可以参考这个。下面说说组成数据的字节顺序。

对于多字节的数据在系统中的存储,通常按数据的高位和低位在系统内存中的高地址和低地址存放分为大端(big endian)和小端(little endian)两种方式。

在Java API中,有ByteOrder这样一个public类,在其中定义了大端和小端两个常量。通过这个java.nio.ByteOrder类的nativeOrder()方法,也可以确定当前系统平台的字节顺序。

在不同的平台上可能有不同的字节顺序标准。但在ByteBuffer类中,默认是使用了ByteOrder.BIG_ENDIAN字节序。但可以通过ByteBuffer的重载方法获取和设置字节序:

  • public final ByteOrder order( )
  • public final ByteBuffer order (ByteOrder bo)

1. ByteBuffer的实现

提到ByteBuffer的实现,我们先来看下Win下JDK实现的类层次结构图。

Win下JDK的ByteBuffer类层次结构图

Win下JDK的ByteBuffer类层次结构图

而在Oracle的Java SE API中,实际上只提到了MappedByteBuffer。所以堆实现和具体的直接实现(DirectByteBuffer)我们只简单了解就行了,因为这个不在API中,和平台实现相关。

HeapByteBuffer 是虚拟机的堆中实现,DirectByteBuffer是系统级别实现(使用unsafe的 unsafe.allocateMemory(size)),使用时后者比前者节省了拷贝过程,但后者的构建和析构成本更高,总体性能需要具体问题综合分析。而且DirectByteBuffer会受到平台方面的约束,使用时需要小心注意。

而API中出现了的java.nio.MappedByteBuffer则是针对文件映射工作的,也是一种Direct的ByteBuffer。除了继承ByteBuffer类的方法外,API还提供了下面3个方法:

  • public final MappedByteBuffer force()
  • public final boolean isLoaded()
  • public final MappedByteBuffer load()

关于文件映射相关的具体内容,下面会详细说。

2. ByteBuffer和Buffer的其它直接子类之间的关系

前面的一篇Buffer的文章提到了它的几个直接子类,分别是ByteBuffer, CharBuffer, DoubleBuffer, FloatBuffer, IntBuffer, LongBuffer, ShortBuffer。除了ByteBuffer,还有另外6种,而这些也都和Java的基本数据类型有一定的对应关系,下面我们对使用上的情况梳理下。

ByteBuffer继承于Buffer。和CharBuffer, DoubleBuffer, FloatBuffer, IntBuffer, LongBuffer, ShortBuffer一样,都是抽象类。

在Java NIO中,除了Buffer还有Channel等类。Buffer也只有和Channel配合才能充分地把Java NIO使用起来。而Channel中的很多方法都是使用ByteBuffer作为参数和返回结果进行传递的,好处就是Byte是字节、是基础,而方法也简单了很多。

而这样的设计有一个要求,就是使用Byte以外的类型也能很好的利用到ByteBuffer,这其中有两种方式:

  • 一种是View Buffer,即直接通过ByteBuffer的数据结构做支持,得到另外一种类型Buffer对象
  • 另一种就是Data Element View,即不通过CharBuffer等类对象和ByteBuffer的互相转换获取,而是直接使用ByteBuffer自带的基本类型put和get方法

下面是两段代码例子。

ByteBuffer byteBuffer = ByteBuffer.allocate (7).order (ByteOrder.BIG_ENDIAN);
CharBuffer charBuffer = byteBuffer.asCharBuffer( );

int value = buffer.getInt( );

当然,在实际使用过程中也会有需要注意的问题,比如字符数据,就需要考虑字符集编码的问题。下面是《Thinking in Java》中的例子:

public class BufferToText {
	private static final int BSIZE = 1024;

	public static void main(String[] args)
			throws Exception {
		FileChannel fc = new FileOutputStream("data2.txt").getChannel();
		fc.write(ByteBuffer.wrap("Some text".getBytes()));
		fc.close();
		fc = new FileInputStream("data2.txt").getChannel();
		ByteBuffer buff = ByteBuffer.allocate(BSIZE);
		fc.read(buff);
		buff.flip();
		// Doesn’t work:
		System.out.println(buff.asCharBuffer());
		// Decode using this system’s default Charset:
		buff.rewind();
		String encoding = System.getProperty("file.encoding");
		System.out.println("Decoded using "+ encoding + ": "+ Charset.forName(encoding)
						.decode(buff));
		// Or, we could encode with something that will print:
		fc = new FileOutputStream("data2.txt").getChannel();
		fc.write(ByteBuffer.wrap("Some text".getBytes("UTF-16BE")));
		fc.close();
		// Now try reading again:
		fc = new FileInputStream("data2.txt").getChannel();
		buff.clear();
		fc.read(buff);
		buff.flip();
		System.out.println(buff.asCharBuffer());
		// Use a CharBuffer to write through:
		fc = new FileOutputStream("data2.txt").getChannel();
		buff = ByteBuffer.allocate(24); // More than needed
		buff.asCharBuffer().put("Some text");
		fc.write(buff);
		fc.close();
		// Read and display:
		fc = new FileInputStream("data2.txt").getChannel();
		buff.clear();
		fc.read(buff);
		buff.flip();
		System.out.println(buff.asCharBuffer());
	}
}

3. 内存映射和ByteBuffer的使用

这段内容将简单说明下文件内存映射的概念和ByteBuffer的其它点。

ByteBuffer的最基本使用,和上一篇NIO中讲CharBuffer等Buffer的直接子类一样,就是put()和get()。为提高效率,除了单个字节读写,有整块的操作方法,即对get()和put()的重载方法。

而内存映射这个概念最初一直困惑了我很久才搞明白,但实际上原理并不复杂,只需要了解操作系统工作的最基本原理。我们通常的直接通过API做IO,会用到一系列的系统调用(system call),之后通过驱动程序和外部设备交互来完成输入输出操作,磁盘上的文件读写也是一样。而文件内存映射之所以得到很好的使用是因为,使用了文件的内存映射可以大大提高效率。提高效率的点就在于,不必每个IO操作都经过系统调用来完成,这个效率是相对较低的,而是巧妙灵活地使用内存管理系统。我们都知道当程序需要使用大量内存而实际物理内存较小的时候,我们的内存管理系统会进行页的换入换出操作,使部分当前使用不到的内存页放到磁盘上去,而缺页中断又会相应的做换入操作 —— 这就是内存映射的基础。

在Java中,在Java NIO的FileChannel类中,提供了一个map()方法,这个方法返回的结果就是一个MappedByteBuffer类的对象,也就是一个ByteBuffer对象。这使得我们对磁盘上文件内容的读写,完全可以像对其他Buffer一样,进行put()和get()。

4. 其它一些实现细节

这是一些未深入整理的实现细节点,在Win下的Oracle/Sun JDK:

  • ByteBuffer的具体实现,也是基于byte数组和对应的offset
  • 有array()和arrayOffset()抽象未实现方法
  • 还有address属性,DirectBuffer才会用到
  • 还有只读等属性和其它方法等

作者原创,难免有错误,欢迎读者热心评论留言指出,以免误导他人,谢谢!

发表在 IO, Java, Java语言, 开发, 计算机技术 | 标签为 , , , , | 18 条评论

欢迎各位捧场,多多使用iPhone手机淘宝聚划算

“小时候淘气,长大了淘宝”到“淘,不出手心”。淘宝网见证了互联网从传统PC到手机端的发展。本人也很有幸赶上了移动互联网发展的机会,投入了手机淘宝的开发。

说到这我还是坦白吧,这是一个宣传贴,欢迎各位有iPhone的朋友多多使用最新版本的手机淘宝客户端。多多浏览、操作,使用淘宝客户端中的聚划算,要是能多多交易就更好了!哈哈……

安装手机淘宝

手机淘宝安装二维码

双十一版本也预计于下周前后发布上架。各位可以先睹为快!

手机淘宝首页

欢迎使用手机淘宝中的聚划算!(手机淘宝首页)

还望各位朋友捧场!多多支持!欢迎帮忙宣传!

如有问题欢迎反馈!谢谢大家!

发表在 iOS, 闲聊杂记 | 标签为 , , , | 8 条评论

说说炎症性肠病和克罗恩病

【本文只是本人的个人理解,描述未必准确,医疗相关内容请咨询医师和专业人士】

本站的一个很重要的目的就是做成一个沟通和分享的平台。本文就分享一下个人经历,希望对各位有同样情况的朋友有所帮助和启发。

去年也就是2012年的下半年,可以说是本人近阶段以来最难熬的半年。说最难熬是因为人生中最重要的一项指标开始进入警报状态——身体健康。

1. 病情

从年中开始,肠胃问题就略显严重。身在号称中国医疗水平最好的北京,历经北京朝阳医院、中日友好医院、北大医院、解放军总医院(301医院)和北京协和医院的反复检查,从几十次抽血化验到CT和胃肠镜,疑似克罗恩病,建议住院。最终考虑疾病严重程度等,未作小肠镜等风险更高的检查操作,得到“慢性肠病,克罗恩病可能性大”的出院结论结论。

依时间的症状发展情况依次是:

  • 长期拉肚子
  • 下午和半夜右下腹剧痛
  • 下午和午夜发烧
  • 体重急剧下降
  • 肛周问题

各项检查结果:

  • 血常规——中性粒细胞偏高、HSCRP偏高、血沉指标不在正常范围
  • B超——胆囊多回声强斑
  • CT增强+小肠重建——多处小肠和回盲肠结合处肠壁增厚、肠系淋巴结肿大
  • 胃镜——浅表胃炎(不严重)
  • 肠镜——回盲肠结合处充血、水肿、变形
  • 肠镜活检——慢性炎症

结论就是,符合IBD特征,克罗恩病可能性大。

何为IBD?IBD就是Inflammatory Bowel Disease,翻译过来就是炎症性肠病。其中最为折磨的两种主要类别就是克罗恩病( CD )和溃疡性结肠炎( UC )。

克罗恩病(CD,Crohn’s Disease)又称克隆氏病、又称局限性肠炎、局限性回肠炎、节段性肠炎和肉芽肿性肠炎。

关于这两种病的详细介绍,可参见专业的解释和各类百科描述。

2. 治疗过程

本人的病症较轻(甚至自己怀疑是否真是IBD)。最严重的时候,大夫建议用激素辅助治疗,但考虑负面影响可能会很大,我拒绝了。

早期是用药甲硝唑/替硝唑进行抗菌,用药3月后停止。

长期服用的是艾迪莎(美沙拉秦缓释颗粒剂)和美常安(枯草杆菌二联活菌肠溶胶囊)。

辅助使用安素(营养粉)和维生素药(因为严重的时候,停止了蔬菜和水果的食用)。

目前用药一年有余,症状逐步在最初半年减轻消失。但口服药一直在吃,本想病已治得差不多了就停药。最终还是坚持按大夫的嘱咐,不能停。

3. 个人治疗过程中的感悟和经验

这个病很多地方介绍都是“病因不明”、“尚无特殊治疗方案”,而我国目前的医疗体系建设又不够完善,治疗过程中遇到的困难和问题都很多。比如去北京的几个三甲医院门诊看病都不是个容易的事情,小医院这方面的经验又很难说。所以如果摊上这个事儿,着急是没用的,只能冷静下来慢慢寻求解决问题的办法。很重要的一点,中国人俗话说“有病乱投医”,现在中国社会条件下,不规范的甚至不合法的“医院”有很多,千万不要受其所害,这种病就要找个权威的正规的医院好好诊断下,一来更早得到结论,二来免受不必要的折磨和痛苦。

对于治病本身,我觉得自己能做的而且确实有很大作用的两点就是“严肃而积极地面对”和“改变生活习惯”。

一直以来我都比较认同一个观点,“心里没病”的人,身体上很难犯什么大的毛病。所以遇到这样一个病的时候,更应该积极地去面对,着急上火只能雪上加霜,对于身体恢复并无益处。虽然这个病“病因不明”“尚无特殊治疗方案”,但明确诊断也不容易。更重要的是,这个病和其它更为乖戾的病比起来,貌似也不会轻易就让人丢掉生命,只是比较折磨。另外,在治疗的过程中,很多问题也只有冷静下来才有清晰的思路和解决方案。

另外就是生活习惯了,除了吃药,这是最积极最直接的“自我治疗方式”。端正态度,把生活和身体健康放在第一位。注意休息,避免劳累。既然是炎症,那就不能吃辛辣、不要吸烟喝酒,避免直接刺激炎症,使其更为严重。适当清理肠道,如果和我一样就先禁食水果蔬菜等纤维素含量高的食物,待炎症逐步消退之后再慢慢补回来。

以上是一些个人体会,仅供参考。

【欢迎各位有经验的医师的指点,也欢迎有类似经历的病友们参与讨论,医疗相关内容请资讯医师和专业人士

发表在 感悟生活, 闲聊杂记 | 标签为 , , | 2 条评论

JavaNIO中Buffer的基本概念

Buffer是Java NIO中最基本的概念,也是java.nio包中的最基础的类。《Java NIO》书中对Buffer的描述是“a holding tank, or staging area”,而在《Thinking in Java》则把Buffer比作穿梭于矿井和外部之间的,运载矿物的货车。实际上,在Java NIO中的设计更为接近操作系统底层的实际运作,运用Buffer也是为了提高IO效率,这篇我们就来看下Buffer相关的最基本的内容。

0.  Buffer

Buffer是Java API在java.nio包中的第一个类,而java.nio包中的类大部分都是Buffer的子类。Buffer类本身是抽象的,甚至连put()、get()方法都没有给出,它主要有这样一些直接子类,分别和非布尔的基本类型对应: ByteBuffer, CharBuffer, DoubleBuffer, FloatBuffer, IntBuffer, LongBuffer, ShortBuffer。

再来看JDK中Buffer类的实现,是围绕对应于Buffer的一个数组展开的,Buffer类中给出了如下4个属性变量:

  • mark 在某些条件下,需要特殊标记position位置时会用到
  • position  指向当前位置的数组index
  • limit 指向可以访问的结尾的数组index,默认等同于capacity
  • capacity 指向数组最大位置的index(+1)

另外,这4个属性变量中前后每两个都恒有<=关系。

和这4个属性对应,Buffer中还提供了一些相关的方法:

  • mark() 标记mark位当前position位置
  • reset() 恢复position到mark位置,前提是mark不为默认值-1
  • position()和position(int) 前者获取当前position值,后者设置position值
  • clear() 恢复到最初的状态,即mark为-1,position为0,limit为capacity,为新一轮channel的读操作做准备
  • flip() 为新一轮写操作做准备,和clear()不同的地方是把limit设置为position,而非capacity
  • rewind() 和flip()类似,不同之处在于不会设置limit到position位置

有意思的是,上述方法大部分都是返回Buffer类型的,即Buffer对象本身,可以用来连续做实例方法调用。

此外,Buffer中有array()和arrayOffset()方法,但是是抽象的未实现的方法。

还有address属性,按注释说明,是Direct Buffer才会用到。

1. CharBuffer等其他基本类型对应的Buffer子类

关于Buffer的子类,我们可以看如下图。

Buffer的子类

Buffer的子类

我们看到这些直接子类都有A标记,也就是说它们都是抽象类。

这些类的具体实现都是Java API之外的,往往都和具体的平台底层结构有关,它们封装了如字节序处理相关等内容。但对于上述这些Buffer子类,我们通常不必关心其具体实现。我们只需要通过allocate()和wrap()方法来分配或者初始化即可。

allocate()和wrap()都是静态方法,在Buffer的子类中有实现。其中allocate()是新分配,而wrap()则可以通过实现实例化的数组作为Buffer的数据存储支持。其中ByteBuffer还有allocateDirect()方法,这个后面的文章详细说明。

对应地,子类也都给出了Buffer类中array()和arrayOffset()的具体实现,在Buffer使用堆中的数组支持的并且非只读情况下,会分别返回数组和偏移量。有hasArray()方法判断前两者的可用性。

还有,最基本地,这些Buffer的直接子类也都给出了各种重载的put()和get()方法,但是是抽象的,具体实现在这些Buffer直接子类的具体子类中实现。如用堆中数组作为数据支持的实现中,put()和get()则分别是对数组进行存取操作。

除了上面所说的,这些子类也基本都提供了这样几个方法:

  • compact() 这个方法也与Buffer标记相关的四个基本属性变量有关,通常是在读了一部分数据之后又开始写数据,把未读的数据拷贝至开头段,并设置各指针准备继续写入。
  • compareTo() 这个方法则与具体的类型有关,Buffer的几个子类基本都是先了Comparable接口。
  • duplicate() 复制,生成一个新的Buffer子类对象,在Buffer个子类中是抽象的;通常在堆实现的情况下,使用的仍是同一个支持数据存储的数组。
  • slice() 和上一个类似,只是角标有所调整。

其它的就是个子类结合类型的情况和实现不同的接口提供的方法了,如CharBuffer实现了Appendable接口,有append()方法等。

2. 其它

上面已经对Buffer和Buffer各子类通用的最基本的内容做了阐述。而在JDK实现上不同平台有所不同,这个就不做细节介绍了,可以参见对应平台的JDK源码。

而ByteBuffer是直接对应字节处理的,比较特殊。关于ByteBuffer的特征,后文会详细整理。

作者原创,难免有错误,欢迎读者热心评论留言指出,以免误导他人,谢谢!

发表在 IO, Java, Java语言, 开发, 计算机技术 | 标签为 , , , , | 留下评论

纪录片《华尔街》观后(上)

“很久以前,他是印第安人的土地;四百年前,他是荷兰人的一道墙;两百年前,他是梧桐树下的金融种子;一百年前,他塑造了美国的崛起。今天,它是一张撒向世界的金融之网,这张网强大又脆弱,光明有黑暗,这张网既能让经济加速,又能让经济窒息。——他就是华尔街!”

这是纪录片《华尔街》的开篇词。

2010年,央视财经推出了一套10集名为《华尔街》的纪录片。央视财经实际上还是能出一些不错的片子的,这个《华尔街》就还很不错。

华尔街

华尔街

这个纪录片讲述了以华尔街为中心,全球经济从萌生到发展,从繁荣到危机的整个过程。对于一个对经济、金融感兴趣而又是非专业的人来说,对金融行业会有更多的认识和了解。

本篇由李成才为导演,吴敬琏和陈志武为总顾问。

下面是前五集的内容概要整理。

1. 资本无眠

本集主要是华尔街简介。

1987年,奥利佛·斯通导演了电影《华尔街》,而2010年又推出了续集《金钱永不眠》,讲述了2008年金融危机中的故事。纪录片《华尔街》以此为开始。

凡事都有两面性,华尔街也是如此,它推动了世界的发展,也会带来灾难。在股票交易所内,充满欺诈和内部交易,但同时如果没有资本市场,美国到现在可能还没有铁路。200年来,华尔街创造的财富远超过了它带来的破坏。

纽约是全球金融中心,而华尔街就坐落在这座城市,它牵动和映像全球资本市场和全球经济。纽约证交所,是全球规模最大的股票交易所。全球每天外汇市场交易额可达2万亿美元,股票成交额每月可达7万亿美元,经济高度全球化。

华尔街交易员需要具备很高的能力,竞争激烈,挑战性强,收入也很丰厚。2007年高升3万员工平均收入66万美元。华尔街刺激着科技创新进步,同时也改变了自身的交易方式。互联网和计算机程序大量得到了应用。

一部好莱坞电影的拍摄,少不了制片人的投资。不仅仅是电影,资金对于很多产业都很重要,而资本市场的主要作用是高效融资和分散风险。

融资来源主要是储蓄和保险金。而投资越来越专业化,个人资金往往是以高度专业的投资机构金融资本市场。投资银行在结合投资者和企业建立联系,在资本市场起着非常重要的作用。高盛等投行被称为金融街的巨人。说道投行,不得不提雷曼兄弟的垮掉以及由此而来的2008经济危机,资本和贪婪是危机的慎重原因。尽管如此,资本市场的积极作用不可否认。

当今,资本交易已经不仅仅是NYSE的开盘到收盘,纽交所停止一天工作后,东京、香港、新加坡又会开始交易,此后又会到伦敦。可以说,金钱无眠!

2. 墙在哪里

本集主要是从Wall Street的名字来源追溯开始,追溯华尔街和全球金融发展历史。

纽约(New York , 新约克)最早称为新阿姆斯特丹,是荷兰的领地。从13世纪开始,荷兰的发展开始走向海洋,阿姆斯特丹成为重要的港口和商品集散中心。但远洋航海的成本和风险都是很高的,这促使了股份制公司的诞生、第一家股票交易所的诞生和兑换储蓄银行的诞生。

同时,荷兰哈德森船长的航海探索发现了现在的纽约,命名为新阿姆斯特丹,并未了保证民众的安全,在城市的北侧建立了围墙(Wall)。

荷兰最先发明了金融系统,但也尝到了金融泡沫苦果,这就是郁金香泡沫。郁金香不再是花,而是人们过热的投资品,加个不断上涨(和今天的高价房地产有类似之处哦)。

1642,英国像新阿姆斯特丹猛攻,荷兰在新阿姆斯特丹和欧洲纷纷战败,英国占领了曼哈顿,并命名为纽约。1688年,英国光荣革命,伦敦逐渐成为世界金融中心。1694,英格兰银行创立。

罗斯柴尔德家族私人银行的诞生和跨国银行的形成对国际金融有重要意义。18世纪,英格兰银行成为最早的中央银行。

最终,美国独立,纽约和华尔街为美国的崛起起了很大作用。

3. 两条道路

这一集主要是讲华尔街和华盛顿之间的关系的。

华尔街和华盛顿,一个是资本市场,一个是政府。后者担心美国会被前者所百步,而前者认为后者没必要限制资本市场的发展。这缘起于美国开国元勋亚历山大·汉密尔顿和托马斯·杰斐逊的分歧。汉密尔顿在开国时的政策推行债务维护,并建立中央银行,杰斐逊则担心政府的过多参与会干扰国家民众的自由。

最终美国没有了中央银行,知道1913年的美联储。而脱离了政府的监管,而华尔街的私人机构独揽的资本市场的大权。华尔街自由生长,华尔街的人们平均每20年会有一次痛苦的经历。西奥多·罗斯福想让华尔街接受政府的监管,而那个时候没有美联储,JP摩根则自己担当起了这个责任,通过让各大银行家出钱,拯救了1907的金融危机。

1913,美联储成立。

4. 镀金时代

本集主要讲华尔街对美国的崛起其到了巨大作用。

无论是伊利运河的修建和纽约的繁荣,还是铁路的修建,华尔街都在融资方面起到了政府不能祈祷的巨大作用,而在内战中华尔街也在北方军需融资过程中做出重大贡献。

除此之外,摩根还帮助爱迪生推广了白炽灯,带领美国进入的电气时代。当然,像摩根这样的金融大佬也证明了资本家的决定权,摩根和爱迪生在一次公司决定中产生了分歧,摩根将爱迪生的公司和另外一家公司合并成立了通用电气,甚至抹去了爱迪生的名字。

华尔街的金融市场的影响力对纽约和美国的发展起到了不可替代的作用。

优酷视频列表:

http://www.soku.com/detail/show/XODk5MTMy

发表在 经济&金融, 观影有感 | 标签为 , , , | 留下评论
第 12 页,共 23 页« 最新...36...1011121314...1821...最旧 »