﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>语源科技BlogJava-cooperzh</title><link>http://www.blogjava.net/cooperzh/</link><description /><language>zh-cn</language><lastBuildDate>Sun, 12 Apr 2026 06:40:39 GMT</lastBuildDate><pubDate>Sun, 12 Apr 2026 06:40:39 GMT</pubDate><ttl>60</ttl><item><title>abstract class MappedByteBuffer extends ByteBuffer</title><link>http://www.blogjava.net/cooperzh/articles/MappedByteBuffer.html</link><dc:creator>cooperzh</dc:creator><author>cooperzh</author><pubDate>Fri, 06 Jan 2012 06:09:00 GMT</pubDate><guid>http://www.blogjava.net/cooperzh/articles/MappedByteBuffer.html</guid><description><![CDATA[<div>MappedByteBuffer 将文件直接映射到虚拟内存。可以映射整个文件，如果文件太大，可以分段指定映射。<br /><br /><div>通常通过FileChannel.map()方法创建。</div>映射之后，通过MappedByteBuffer 访问文件内容，比到硬盘上去读取文件要快很多。<br /><br /><div>FileChannel.map()方法创建时指定方式：<br /><div><div>MapMode.READ_ONLY，尝试修改缓冲区则报异常ReadOnlyBufferException<div>MapMode.READ_WRITE，共享缓冲区，所有访问的程序都可读可写，但写完是否其他程序立即看到变更，未知<br /><div>MapMode.PRIVATE，创建副本，所有修改对同时访问的其他程序不可见<br /><br />protected：<br />volatile boolean isAMappedBuffer;<br /><div>MappedByteBuffer(int mark,int pos,int lim,int cap,boolean mapped);</div><div>MappedByteBuffer(mark,pos,lim,cap);</div><br />private方法：<br />checkMapped();&nbsp;<br />pagePosition();<br /><br />public final方法：<br /><span style="color: red; ">isLoaded(); 缓存区内容是否处于物理内存中</span><br /><span style="color: red; ">load(); 将缓冲区内容从虚拟内存加载到物理内存</span><br /><span style="color: red; ">force(); 当缓存区是</span><span style="color: red; ">MapMode.READ_WRITE模式时，将缓存区内容写入存储设备中</span><br /><br />private native方法：<br />isLoaded0();<br />load0();<br />force0();<br /><br /><br /><br /><br /><br /><br /><br /><br /></div></div></div></div></div></div><img src ="http://www.blogjava.net/cooperzh/aggbug/368009.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/cooperzh/" target="_blank">cooperzh</a> 2012-01-06 14:09 <a href="http://www.blogjava.net/cooperzh/articles/MappedByteBuffer.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>接口 Readable</title><link>http://www.blogjava.net/cooperzh/articles/Readable.html</link><dc:creator>cooperzh</dc:creator><author>cooperzh</author><pubDate>Fri, 06 Jan 2012 03:01:00 GMT</pubDate><guid>http://www.blogjava.net/cooperzh/articles/Readable.html</guid><description><![CDATA[<div>Readable是一个字符源<br /><br />public方法：<br />read(CharBuffer); 将字符读入指定的缓冲区</div><img src ="http://www.blogjava.net/cooperzh/aggbug/367991.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/cooperzh/" target="_blank">cooperzh</a> 2012-01-06 11:01 <a href="http://www.blogjava.net/cooperzh/articles/Readable.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>接口 Appendable</title><link>http://www.blogjava.net/cooperzh/articles/Appendable.html</link><dc:creator>cooperzh</dc:creator><author>cooperzh</author><pubDate>Fri, 06 Jan 2012 02:58:00 GMT</pubDate><guid>http://www.blogjava.net/cooperzh/articles/Appendable.html</guid><description><![CDATA[表示可以向char序列添加字符(有效的Unicode字符)<br /><br />方法有：<br />append(char);<br />append(CharSequence);<br />append(CharSequence csq, int start, int end);<img src ="http://www.blogjava.net/cooperzh/aggbug/367990.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/cooperzh/" target="_blank">cooperzh</a> 2012-01-06 10:58 <a href="http://www.blogjava.net/cooperzh/articles/Appendable.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>接口 Comparable&lt;T&gt;</title><link>http://www.blogjava.net/cooperzh/articles/Comparable.html</link><dc:creator>cooperzh</dc:creator><author>cooperzh</author><pubDate>Thu, 05 Jan 2012 15:54:00 GMT</pubDate><guid>http://www.blogjava.net/cooperzh/articles/Comparable.html</guid><description><![CDATA[<div><span style="font-size: medium; background-color: #ffffff; "><h3><br /><span class="Apple-style-span" style="font-family: Simsun; line-height: normal; font-weight: normal; ">public 方法</span><br /><span style="font-weight: normal; "><font face="Simsun"><span style="line-height: normal;">compareTo方法：比较两个对象的内容</span></font><br /><font face="Simsun"><span style="line-height: normal;">返回值由实体类具体定义，通常为-1,0,1</span></font><br /><br /><font face="Simsun"><span style="line-height: normal;">实现了该接口的实体类就有了彼此比较的能力</span></font><br /><br /><font face="Simsun"><span style="line-height: normal; ">实现了该接口的实体类的数组，在使用sort()即可实现自动排序。因为sort内部会调用</span></font>compareTo()来比较大小。这就是实现Comparable接口的好处</span></h3></span></div><img src ="http://www.blogjava.net/cooperzh/aggbug/367974.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/cooperzh/" target="_blank">cooperzh</a> 2012-01-05 23:54 <a href="http://www.blogjava.net/cooperzh/articles/Comparable.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>《java解惑》。。。</title><link>http://www.blogjava.net/cooperzh/archive/2012/01/05/java_puzzlers.html</link><dc:creator>cooperzh</dc:creator><author>cooperzh</author><pubDate>Thu, 05 Jan 2012 15:35:00 GMT</pubDate><guid>http://www.blogjava.net/cooperzh/archive/2012/01/05/java_puzzlers.html</guid><description><![CDATA[<br />19 块注释(/* &nbsp;*/)不能可靠的注释掉代码块，应用单行注释符//<img src ="http://www.blogjava.net/cooperzh/aggbug/367973.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/cooperzh/" target="_blank">cooperzh</a> 2012-01-05 23:35 <a href="http://www.blogjava.net/cooperzh/archive/2012/01/05/java_puzzlers.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>接口 CharSequence</title><link>http://www.blogjava.net/cooperzh/articles/CharSequence.html</link><dc:creator>cooperzh</dc:creator><author>cooperzh</author><pubDate>Thu, 05 Jan 2012 13:24:00 GMT</pubDate><guid>http://www.blogjava.net/cooperzh/articles/CharSequence.html</guid><description><![CDATA[<div>
<div>CharSequence 是 char 值的一个可读序列<br />
<br />
最基本的char存储协议<br />
<br /><h2 style="background-color: #cccccc; ">
方法：</h2><ol><li>charAt(int index);<br />返回长度是序列中16位char的数量</li><li>length();</li><li>subSequence(int start, int end);</li><li>toString();</li></ol></div>
</div><img src ="http://www.blogjava.net/cooperzh/aggbug/367950.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/cooperzh/" target="_blank">cooperzh</a> 2012-01-05 21:24 <a href="http://www.blogjava.net/cooperzh/articles/CharSequence.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>《JVM调优总结.pdf》。。。</title><link>http://www.blogjava.net/cooperzh/archive/2011/12/27/367353.html</link><dc:creator>cooperzh</dc:creator><author>cooperzh</author><pubDate>Tue, 27 Dec 2011 09:36:00 GMT</pubDate><guid>http://www.blogjava.net/cooperzh/archive/2011/12/27/367353.html</guid><description><![CDATA[作者blog:<a href="http://pengjiaheng.iteye.com/">http://pengjiaheng.iteye.com/</a><br /><br />数据类型：基本类型和引用类型<br />基本类型的变量保存原始值，它代表的值就是数值本身。byte,short,int,long,char,float,double,Boolean,returnAddress<br />引用类型代表某个对象的引用，存放引用值的地址。类类型，接口类型，数组<br /><br />栈stack 和 堆heap:<br />stack是运行时单位，每个线程都会有线程栈与之对应。里面存储的是当前线程相关信息。包括局部变量，程序运行状态，方法返回值等<br />heap是存储数据的地方，所有线程共享。存放对象信息。<br /><br />1 从软件设计的角度看，stack代表处理逻辑，heap代表数据。<br />2 heap的数据被多个线程共享，则多个线程可以访问同一对象。heap的数据可以供所有stack访问，节省了空间<br />3 stack因为运行时的需要，保存系统运行的上下文，需要进行地址段的划分。并且只能向上增长，因此限制了stack的存储能力。而heap中的数据可以根据需要动态增长，相应stack中只需要记录heap中的一个地址即可。<br />4 面向对象就是stack和heap的完美结构。对象的属性就是数据，存放在heap中。而对象的方法就是运行逻辑，放在stack中。<br /><br />在java中，main函数就是stack的起点，也是程序的起点。<br /><br />heap中存放的是对象实例，stack中是基本数据类型和heap中对象的引用。一个对象的大小是不可以估计的，甚至动态变化的。但是在stack中，一个对象只对应了一个4byte的引用。这就是stack和heap分离的好处。<br /><br />因为基本数据类型占用的空间是1到8个字节，需要空间比较小，而且不会出现动态增长的情况，因此stack中存储就够了。<br /><br />java中参数传递时传值还是传引用？<br />程序永远在stack中运行，因而参数传递的只是基本类型或者对象的引用。不会传递对象本身。<br />简单说，java在方法调用传递参数时，都是进行传值调用。<br /><span style="color: red; ">当传递引用的时候，程序会将引用查找到heap中的那个对象，这个时候进行修改，修改的是真实的对象，而不是引用本身！！！</span><br />所以传递引用也是传递的最终值。<br /><span style="color: red; ">另外，因为传递基本类型是传递了基本值，所以修改的也是另一个copy，而无法修改原值。只有传递的是对象引用时才能修改原对象。</span><br /><br />stack是程序运行最根本的东西。程序运行可以没有heap，但必须有stack。<br /><br />java中，stack的大小是通过-Xss来设置的，当stack中数据比较多时，需要适当调大这个值，否则会出现java.long.StackOverflowError异常<br /><br /><br />Java对象的大小<br />一个空object的大小是8byte，这个大小只是保存heap中一个没有任何属性的对象大大小。如：<br />Object o = new Object();<br />它所占的空间为4byte + 8byte。4byte为stack中保存对象引用需要的空间，8byte是heap中对象的信息。<br />因为所有java非基本类型对象都是集成自Object，所以不论什么样的java对象，大小都必须大于8byte<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #008080; ">1</span>&nbsp;<br /><span style="color: #008080; ">2</span>&nbsp;Class&nbsp;NewObject{<br /><span style="color: #008080; ">3</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;count;<br /><span style="color: #008080; ">4</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;flag;<br /><span style="color: #008080; ">5</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Object&nbsp;o;<br /><span style="color: #008080; ">6</span>&nbsp;}</div><span style="color: red; ">其大小为：对象大小(8) + int型(4) + boolean(1) + 对象引用(4) = 17byte</span><br /><span style="color: red; ">但是因为java在内存中对对象进行分配时都是以8的倍数来分配，因此会为NewObject对象实例分配 24byte。</span><br /><br />需要注意基本类型的包装类型的大小。因为包装类型已经成为对象了，因此要把包装类型当对象来看待。如Integer,Float,Double等<br />一个包装类型最少占用16byte(8的倍数)，它是使用基本类型的N倍，因此尽量少用包装类。<br /><br />对象引用分为：强引用，软引用，弱引用和虚引用。<br />1 强引用StrongReference：我们一般声明对象时jvm生成的引用，强引用环境下，垃圾回收需要严格判断。如果被强引用则不会被回收<br />2 软引用SoftReference：一般作为缓存来使用。在垃圾回收的时候，jvm会根据当前系统的剩余内存来决定是否对软引用进行回收。如果jvm发生outOfMemory时，肯定是没有软引用存在的。<br />3 弱引用WeakReference：与软引用类似，都是作为缓存来使用。不同的是在垃圾回收的时候，弱引用一定会被回收。因此其生命周期只存在一个垃圾回收周期内。<br />4 虚引用PhantomReference：形同虚设，随时会被垃圾回收。其主要功能是与引用队列(ReferenceQueue)联合使用。当垃圾回收发现一个对象有虚引用时，就会在回收内存之前，将虚引用添加到与之关联的引用队列中。程序可以通过判断引用队列中是否有虚引用来了解引用对象是否将要被回收。从而决定是否采取行动。<br /><br />系统一般使用强引用。软引用和弱引用一般是在内存大小比较受限的情况下使用。常用在桌面引用系统中。<br /><br /><br />垃圾回收基本算法<br />1 引用计数 Reference Counting<br />古老的回收算法。对象多一个引用，就增加一个计数，删除一个引用则减少一个计数。垃圾回收时，只收集计数为0的对象。<br />该算法最致命的是无法处理循环引用的问题。<br />2 标记-清除 Mark-Sweep<br />此算法分为两个阶段。第一阶段从引用根节点开始标记所有被引用的对象，第二阶段遍历整个heap，把未标记的对象清除。<br />此算法需要暂停整个应用。同时产生内存碎片。<br />3 复制 Copying<br />此算法把空间划分为2个相等的区域。每次只使用其中一个区域。垃圾回收时，遍历当前使用区域，把正在使用中的对象复制到另一个区域中。此算法每次只处理正在使用中的对象，因此复制成本比较小，同时复制过去以后还能进行相应的内存整理，不会出现碎片。<br />此算法的缺点是需要两倍的内存空间。<br />4 标记-整理 Mark-Compact<br />此算法结合了 Mark_Sweep 和 Copying 两个算法的优点。<br />第一阶段从根节点开始标记所有被引用对象，第二阶段遍历整个heap，清除未标记对象并且把存活对象压缩到heap的其中一块，按照顺序摆放。<br />此算法避免了碎片问题，和Copying算法的空间问题。<br /><br />5 增量收集 Incremental Collecting&nbsp;<br />实时垃圾回收算法，在应用进行的同时进行垃圾回收，jdk5 没有使用此算法<br />6 分代收集 Generational Collecting<br />基于对对象生命周期分析后得出的垃圾回收算法。把对象分为年青代、年老代、持久代，对不同周期的对象采用不同的算法（上面算法的一个）进行回收。<br /><br />7 串行收集<br />使用单线程处理所有垃圾回收工作，因为无序多线程交互，更容易实现，而且效率很高。但是无法使用多处理器的优势，所以只适合单处理器的机器。<br />8 并行收集<br />使用多线程处理垃圾回收工作速度快，效率高。理论上cpu数目越多，越能体现并行收集的优势<br />9 并发收集<br />前面两个在进行垃圾回收的时候，需要暂停整个运行环境。因此系统会有明显的暂停，暂停时间因为heap越大而越长。<br /><br /><br />垃圾回收面临的问题<br /><br />1 如何区分垃圾？<br />因为引用计数无法解决循环引用。所有后来的垃圾回收算法都是从程序运行的根节点出发，遍历整个对象引用，查找存活的对象。因为stack是真正进行程序执行的地方，所以要知道哪些对象正在被使用，需要从java stack开始。如果有多个线程，必须对这些线程对应的所有stack进行检查。<br />除了stack外，还有系统运行时的寄存器，也是存储程序运行时数据的地方。这样以stack和寄存器中的引用为起点，来找到heap中的对象，又从这些对象中找到对heap中其他对象的引用，这样逐步扩展。最终以null引用或基本类型结束，这样就形成了一棵以java stack中引用所对应的对象为根节点的一棵对象数。如果stack中有多个引用，则最终形成多棵对象树。这些对象树上的对象，都是当前系统运行所需要的对象，不能被回收。而其他剩余对象，视为无法被引用的对象，可以被回收。<br />因此垃圾回收的起点是一些根对象(java stack，static 变量，寄存器&#8230;&#8230;)。最简单的java stack就是main函数。这种回收方式，就是Mark-Sweep。<br /><br />2 如何处理碎片？<br />因为不同java对象存活时间不同，因此程序运行一段时间后，会出现零散的内存碎片。碎片最直接的问题就是导致无法分配大块的内存空间，以及程序运行效率降低。Copying和Mark-Compact都可以解决碎片问题<br /><br />3 如何解决同时存在的对象创建和对象回收问题<br />垃圾回收线程是回收内存的，程序运行是消耗内存的，一个回收内存，一个分配内存，两者是毛段的。因此，在现有的垃圾回收方式中，在垃圾回收前，一般都需要暂停整个应用（暂停内存分配），然后进行垃圾回收，回收完成后再继续应用。<br />这样的弊端是，当heap空间持续增大时，垃圾回收的时间也将相应增长，相应的程序暂停时间也增长。一些对时间要求很高的应用，比如最大暂停时间要求是几百ms，那么当heap空间大于几个G时，就可能超时。这种情况下，垃圾回收会成为系统运行的一个瓶颈。为了解决这个矛盾，有了并发垃圾回收算法。使用这个算法，垃圾回收线程与程序运行线程同时运行。没有暂停，算法复杂性会大大增加，系统处理能力也相应降低。同时碎片问题将会比较难解决。<br /><br /><br />分代垃圾回收详述：<br />1 为什么要分代？<br />基于这样一个事实：不同对象的生命周期是不一样的。因此不同生命周期的对象可以采取不同的收集方式，以便提高回收效率。<br />在java运行过程中会产生大量对象。其中有些对象是与业务信息相关的。比如http请求中的session对象、线程、socket连接，这些跟业务直接挂钩，因此生命周期比较长。但是程序运行过程中生成的临时变量，生命周期会比较短。比如String对象等。<br /><br />2 如何分代？<br />jvm中共划为三个代：年轻代(Young Generation)、年老代(Old Generation)、持久代(Permanent Generation)<br />持久代主要存放Java类信息，与垃圾回收要收集的java对象关系不大。年青代和年老代是对垃圾收集影响最大的。<br /><br />年轻代：<br />1 所有新生成的对象首先都是放在&#8220;年轻代"里的。年轻代的目标就是尽可能快速的收集那些生命周期短的对象。<br />年轻代分为三个区：1个Eden区，2个Survivor(幸存，残余)区<br /><span style="color: red; ">大部分对象在Eden区生成，当Eden区满时，还存活的对象将被复制到Survivor1区，当Survivor1区满时，此区的存活对象将被复制到Survivor2区，此时，</span><span style="color: red; ">Eden区满时还存活的对象将复制到Survivor2中，</span><span style="color: red; ">Survivor1区会被清空</span><span style="color: red; ">。</span><span style="color: red; ">当Survivor2区也满时（包含从1中复制过来的对象和从Eden区过来的对象），从Survivor1区复制过来的并且还存活的对象，将被复制到"年老代"的年老区(Tenured Space)。Survivor2区中新增加的从Eden区过来的还存活的对象，将复制到Survivor1中，Survivor2被清空。之后，</span><span style="color: red; ">Eden区满时还存活的对象就会复制到Survivor1中。重复这样的循环。</span><br />两个Survivor区是对称的，没有先后顺序。所以同一个区中可能同时存在从Eden复制过来的对象和另一个Survivor区复制过来的对象。<br />而复制到年老代的Tenured区的只有从第一个Survivor区过来的对象，因为Tenured区存放的是从第一个Survivor区过来，依旧存活的对象。<br />两个Survivor区中总有一个是空的。同时根据需要，可以配置多个Survivor区，延长对象在年轻代中的存在时间，减少被放到年老代的可能。<br />2 年老代<br />在年轻代中经历了N次垃圾回收后仍然存活的对象，就会被放到年老代。因此年老代存放的都是生命周期较长的对象。<br />3 持久代<br />用于存放静态文件，如java类，方法等。<br />持久代对垃圾回收没有显著影响，但是有些应用可能动态生成或者调用一些class，例如Hibernate等，在这种时候需要设置一个比较大的持久代空间来存放这些运行过程中新增的类。持久代大小通过-XX:MaxPermSize=&lt;N&gt;进行设置。<br /><br />什么情况下触发垃圾回收？<br />由于对象进行了分代处理，因此垃圾回收区域、时间也不一样。<br />GC(Garbage Collection)有两种类型：Scavenge GC 和 Full GC<br />1&nbsp;Scavenge GC&nbsp;<br />一般情况下，当新对象生成，并且在Eden区申请空间失败时，就会触发Scavenge GC。对Eden区进行GC，清除非存活对象，并且把尚且存活的对象移动到Survivor区。然后整理Survivor的两个区。这种方式的GC是对年轻代的Eden区进行，不会影响到年老代。<br />因为大部分对象都是从Eden区开始的，同时Eden区不会分配的很大，所以Scavenge GC 会频繁进行。<br />一般这里需要使用速度快，效率高的算法，使Eden区尽快空闲出来。<br />2 Full GC<br />对整个Heap进行整理，包括年轻代，年老代和持久代。Full GC因为需要对整个进行回收，所以比Scavenge GC要慢，因此应该尽可能减少Full GC的次数。在对jvm调优的过程中，很大一部分工作就是对Full GC的调节。导致Full GC是因为如下方法：<br /><ul><li>年老代的Tenured区被写满<br /></li><li>持久代被写满</li><li>System.gc()被显式调用</li><li>上一次GC之后Heap的各区域分配策略动态变化</li></ul><br />选择合适的垃圾收集算法<br />1 串行收集器<br />用单线程处理所有垃圾回收工作，因为无需多线程交互，所以效率比较高。但是，无法使用多处理器的优势，所以只适合单处理器机器。<br />也可以用在小数据量(100M)情况下的多处理器机器上。用-XX:+UseSerialGC打开<br /><br />2 并行收集器<br />对年轻代进行并行垃圾回收，因此可以减少垃圾回收时间。一般在多线程多处理器上使用。使用-XX:+UseParallelGC打开。<br />并行收集器在J2SE5.0更新上引入，在java SE6.0中进行了增强，可以对年老代进行收集。如果年老代不使用并发收集的话，默认是使用单线程进行垃圾回收，因此会制约扩展能力。使用-XX:+UseParallelOldGC打开。<br />设置：<br /><ul><li>并行垃圾回收的线程数，使用-XX:ParallelGCThreads=&lt;N&gt;。此值可以设置与机器处理器数量相等。</li><li>最大垃圾回收暂停，指定垃圾回收时的最长暂停时间，通过-XX:MaxGCPauseMillis=&lt;N&gt;指定。N为毫秒，如果指定了此值，heap大小和垃圾回收相关参数会进行调整以达到指定值。设定此值会减少应用吞吐量。<br /></li><li>吞吐量，为垃圾回收时间与非垃圾回收时间的比值，通过-XX:GCTimeRatio=&lt;N&gt;来设定，公式为1/(1+N)。例如，N=19时，表示5%的时间用于垃圾回收。默认值是99，即用1%的时间用于垃圾回收。<br /></li></ul>3 并发收集器<br />可以保证大部分工作都并发进行(应用不停止)，垃圾回收只暂停很少的时间，此收集器适合对响应时间要求比较高的大中规模应用。<br />使用-XX:+UseConcMarkSweepGC打开。<br />并发收集器主要减少年老代的暂停时间，它在应用不停止的情况下使用独立的垃圾回收线程，跟踪可达对象。在每个年老代垃圾回收周期中，在收集出气并发收起会对整个应用进行简短的暂停，在收集中还会再暂停一次。第二次暂停时间比第一次稍长，在此过程中多个线程同时进行垃圾回收工作。<br />并发收集器使用处理器换来短暂的停顿时间。<br />在一个N个处理器的系统上，并发收集部分使用K/N个可用处理器进行回收，一般情况下1&lt;=K&lt;=N/4。即K小于N的四分之一。<br />在只有一个处理器的主机上使用并发收集器，设置为incremental mode模式也可以获得较短的停顿时间。<br /><br />浮动垃圾(Floating Garbage)：<br />由于在应用运行的同时进行垃圾回收，所以有些垃圾可能在垃圾回收进行完成时产生，这样就造成了&#8220;Floating Garbage&#8221;，这些垃圾需要在下次垃圾回收周期时才能回收掉。所有，并发收集器一般需要预留20%的空间用于浮动垃圾。<br /><br />并发模式失败(Concurrent Mode Failure):<br />并发收集器在应用运行时进行收集，所以需要保证heap在垃圾回收的这段时间有足够的空间供程序使用，否则，垃圾回收还未完成，heap空间先满了。这种情况下就会发生并发模式失败，此时整个应用会暂停，进行垃圾回收。<br />为了保证有足够内存供并发收集器使用，可以设置-XX:CMSInitiatingOccupancyFraction=&lt;N&gt;指定剩余多少heap时开始执行并发收集。<br /><br />1 串行serial收集器，适用于数据量比较小(100M)的应用，或者单处理器下并且对响应时间无要求的应用。<br />缺点：只能用于小型应用<br />2 并行parallel收集器，适用于对吞吐量有高要求，多cpu，对应用相应时间无要求的大中型应用。如后台处理、科学计算。<br />缺点：垃圾收集过程中应用响应时间可能加长<br />3 并发concurrent收集器：适用于对响应时间有高要求，多cpu的大中型应用。如web服务器、应用服务器、电信交换、集成开发环境。<br /><br /><br /><br />P26&#8230;&#8230;<br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><img src ="http://www.blogjava.net/cooperzh/aggbug/367353.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/cooperzh/" target="_blank">cooperzh</a> 2011-12-27 17:36 <a href="http://www.blogjava.net/cooperzh/archive/2011/12/27/367353.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>《构建高性能的大型分布式Java应用》笔记 第一章 IO</title><link>http://www.blogjava.net/cooperzh/archive/2011/12/27/367323.html</link><dc:creator>cooperzh</dc:creator><author>cooperzh</author><pubDate>Tue, 27 Dec 2011 03:50:00 GMT</pubDate><guid>http://www.blogjava.net/cooperzh/archive/2011/12/27/367323.html</guid><description><![CDATA[IO三种方式：BIO，NIO，AIO<span style="font-size: 10pt; ">&nbsp;(异步读写asynchronous IO)</span><br /><br /><span style="font-size: 10pt; ">jdk1.6及之前都只实现BIO 和 NIO</span><br /><span style="font-size: 10pt; ">jdk1.7开始支持AIO，即NIO 2.0</span><br /><br /><br /><span style="font-size: 10pt; ">在BIO阻塞模式下server端:</span><br /><span style="font-size: 10pt; ">1 new ServerSocket(int port) 监听端口</span><br /><span style="font-size: 10pt; ">2 serverSocket.accept() 阻塞式等待客户端的连接，有连接才返回Socket对象</span><br /><span style="font-size: 10pt; ">3 socket.getINputStream() 获取客户端发过来的信息流</span><br /><span style="font-size: 10pt; ">4 socket.getOutputStream() 获取输出流对象，从而写入数据返回客户端</span><br /><br /><span style="font-size: 10pt; ">client端：</span><br /><span style="font-size: 10pt; ">1 newSocket（String host,int port) 建立与服务器端的连接，如果服务器没启动，报Connection refused异常</span><br /><span style="font-size: 10pt; ">2 socket.getInputStream() 读取服务器端返回的流</span><br /><span style="font-size: 10pt; ">3 socket.getOutputStream() 获取输出流，写入数据发送到服务器端</span><br /><br /><br /><span style="font-size: 10pt; ">在NIO模式下Server端：</span><br /><span style="font-size: 10pt; ">1 ServerSocketChannel.open() 获取serverScoketChannel实例</span><br /><span style="font-size: 10pt; ">2 serverScoketChannel.configueBlocking(false) 设置channel为非阻塞模式</span><br /><span style="font-size: 10pt; ">3 serverSocketChannel.socket() 获取serverSocket对象</span><br /><span style="font-size: 10pt; ">4 serverSocket.bind(port) 监听端口</span><br /><span style="font-size: 10pt; ">5 Selector.open() 打开Selector，获取selector实例</span><br /><span style="font-size: 10pt; ">6 serverSocketChannel.register(Selector,int) 向selector注册channel和感兴趣的事件</span><br /><span style="font-size: 10pt; ">7 while(true) 循环以保证正常情况下服务器端一直处于运行状态</span><br /><span style="font-size: 10pt; ">8 selector.select() 获取selector实例中需要处理的SelectionKey的数量</span><br /><span style="font-size: 10pt; ">9 for(SelectionKey key:selector.selectedKeys()) 遍历selector.selectedKeys,以对每个SelectionKey的事件进行处理</span><br /><span style="font-size: 10pt; ">10 key.isAcceptable() 判断SelectionKey的类型是否为客户端建立连接的类型</span><br /><span style="font-size: 10pt; ">11 key.channel() 当SelectionKey的类型是acceptabel时，获取绑定的ServerSocketChannel对象</span><br /><span style="font-size: 10pt; ">12 serverSocketChannel.accept() 接受客户端建立连接的请求，并返回SocketChannel对象</span><br /><span style="font-size: 10pt; ">13 socketChannel.regiseter(Selector,int) 向Selector注册感兴趣的事件类型，如read,write</span><br /><span style="font-size: 10pt; ">14 key.isReadable() 判断SelectionKey是否为readable，如是则意味着有消息流在等待处理</span><br /><span style="font-size: 10pt; ">15 socketChannel.read(ByteBuffer) 从SelectionKey中绑定的SocketChannel对象读取消息流</span><br /><span style="font-size: 10pt; ">16 socketChannel.write(ByteBuffer) 从SelectionKey中绑定的SocketChannel对象输出消息流</span><br /><br /><div><span style="font-family: Tahoma; font-size: 10pt; ">client端：</span></div><div><span style="font-size: 10pt; ">1 SocketChannel.open() 打开SocketChannel</span></div><div><span style="font-size: 10pt; ">2 SocketChannel.configureBlocking(false) 将SocketChannel配置为非阻塞模式</span></div><div><span style="font-size: 10pt; ">3 SocketChannel.connect(host,port) 连接到指定的目标地址</span></div><div><span style="font-size: 10pt; ">4 Selector.open() 打开Selector</span></div><div><span style="font-size: 10pt; ">5 SocketChannel.register(Selector,int) 向Selector注册感兴趣的事件,connected,read,write</span></div><div><span style="font-size: 10pt; ">6 while(true) 循环执行保证客户端一直处于运行状态</span></div><div><span style="font-size: 10pt; ">7 Selector.select() 从Selector中获取是否有可读的key信息</span></div><div><span style="font-size: 10pt; ">8 for(SelectionKey key:selector.selectedKeys()) 遍历selector中所有selectedKeys</span></div><div><span style="font-size: 10pt; ">9 SelectionKey.isConnectable() 判断是否为连接建立的类型</span></div><div><span style="font-size: 10pt; ">10 SelectionKey.channel() 获取绑定的SocketChannel</span></div><div><span style="font-size: 10pt; ">11 SocketChannel.finishConnect() 完成连接的建立（TCP/IP的三次握手）</span></div><div><span style="font-size: 10pt; ">12 SelectionKey.isReadable() 判断是否为可读类型</span></div><div><span style="font-size: 10pt; ">13 SelectionKey.channel() 获取绑定的SocketChannel</span></div><div><span style="font-size: 10pt; ">14 SocketChannel.read(ByteBuffer) 从SocketChannel中读取数到ByteBuffer中</span></div><div><span style="font-size: 10pt; ">15 SocketChannel.write(ByteBuffer) 向SocketChannel中写入ByteBuffer对象数据</span></div><div style="font-family: Tahoma; font-size: 11px; "></div><img src ="http://www.blogjava.net/cooperzh/aggbug/367323.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/cooperzh/" target="_blank">cooperzh</a> 2011-12-27 11:50 <a href="http://www.blogjava.net/cooperzh/archive/2011/12/27/367323.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Tricks and Tips With AIO Part 1: The Frightening Thread Pool （转载）</title><link>http://www.blogjava.net/cooperzh/archive/2011/12/22/367000.html</link><dc:creator>cooperzh</dc:creator><author>cooperzh</author><pubDate>Thu, 22 Dec 2011 04:01:00 GMT</pubDate><guid>http://www.blogjava.net/cooperzh/archive/2011/12/22/367000.html</guid><description><![CDATA[from:&nbsp;<a href="http://java.dzone.com/articles/hold-until-dec-8-tricks-and-ti" title="Tricks and Tips With AIO Part 1: The Frightening Thread Pool">Tricks and Tips With AIO Part 1: The Frightening Thread Pool<br /><font color="#000000"><br /></font><font color="#000000"><br /></font></a>Jean-Francois&nbsp;<br /><br /><div><div>A while ago JDK 1.4 introduced the notion of non-blocking I/O. With non-blocking I/O (NIO), you're getting events through a selector when there is some I/O ready to be processed, like read and write operations. before JDK 1.4, only blocking I/O was available. With locking I/O, you were just blocking on a stream, trying to read and write.</div><div></div><div>JDK 7 introduces asynchronous I/O (AIO). Asynchronous I/O gives you a notification when the I/O is completed. The big difference with non-blocking is with AIO you get the notification when the I/O operation complete, where with blocking you you get notified when the I/O operation is ready to be completed.</div><div></div><div>For example, with a socket channel in a non-blocking mode, you register with a selector, and the selector will give you a notification when there is data on that socket to read. With the asynchronous I/O, you actually start the read, and the I/O will complete sometime later when the read has happened and there is data in your byte buffer.</div><div></div><div>With AIO, you wait for completed I/O operation using we a completion handler (explained in details below). You specify a completion handler when you do your read, and the completion handler is invoked to tell you that the I/O operation has completed with the bytes that has been read. With non-blocking, you would have been notified and then you would have executed yourself the read operation to read bytes.</div><div></div><div>One of the nice thing you can do with AIO is to configure yourself the thread pool the kernel will uses to invoke a completion handler. A completion handler is an handler for consuming the result of an asynchronous I/O operation like accepting a remote connection or reading/writing some bytes. So an asynchronous channels (with NIO.1 we had SelectableChannel) allow a completion handler to be specified to consume the result of an asynchronous operation. The interface define three "callback":</div><div></div><div>&nbsp; &nbsp;* completed(...): invoked when the I/O operation completes successfully.</div><div>&nbsp; &nbsp;* failed(...): invoked if the I/O operations fails (like when the remote client close the connection).</div><div>&nbsp; &nbsp;* cancelled(...): invoked when the I/O operation is cancelled by invoking the cancel method.</div><div></div><div>Below is an example (I will talk about it it much more details in part II) of how you can open a port and listen for requests:</div></div><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #008080; ">&nbsp;1</span>&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;Open&nbsp;a&nbsp;port</span><span style="color: #008000; "><br /></span><span style="color: #008080; ">&nbsp;2</span>&nbsp;<span style="color: #008000; "></span><span style="color: #0000FF; ">final</span>&nbsp;AsynchronousServerSocketChannel&nbsp;listener&nbsp;=<br /><span style="color: #008080; ">&nbsp;3</span>&nbsp;&nbsp;&nbsp;&nbsp;AsynchronousServerSocketChannel.open().bind(<span style="color: #0000FF; ">new</span>&nbsp;InetSocketAddress(port));<br /><span style="color: #008080; ">&nbsp;4</span>&nbsp;<br /><span style="color: #008080; ">&nbsp;5</span>&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;Accept&nbsp;connections</span><span style="color: #008000; "><br /></span><span style="color: #008080; ">&nbsp;6</span>&nbsp;<span style="color: #008000; "></span>listener.accept(<span style="color: #0000FF; ">null</span>,&nbsp;<span style="color: #0000FF; ">new</span>&nbsp;CompletionHandler&lt;AsynchronousSocketChannel,Void&gt;()&nbsp;{<br /><span style="color: #008080; ">&nbsp;7</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;completed(AsynchronousSocketChannel&nbsp;channel,Void&gt;&nbsp;result)&nbsp;{<img src="http://www.blogjava.net/Images/dot.gif" alt="" />}<br /><span style="color: #008080; ">&nbsp;8</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;cancelled(AsynchronousSocketChannel&nbsp;channel,Void&gt;&nbsp;result)&nbsp;{<img src="http://www.blogjava.net/Images/dot.gif" alt="" />}<br /><span style="color: #008080; ">&nbsp;9</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;failed(AsynchronousSocketChannel&nbsp;channel,Void&gt;&nbsp;result)&nbsp;{<img src="http://www.blogjava.net/Images/dot.gif" alt="" />}<br /><span style="color: #008080; ">10</span>&nbsp;}<br /><span style="color: #008080; ">11</span>&nbsp;</div><div><p style="margin-top: 0.6em; margin-right: 0px; margin-bottom: 1.2em; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-width: 0px; border-style: initial; border-color: initial; border-image: initial; outline-width: 0px; outline-style: initial; outline-color: initial; font-size: 12px; font-family: arial, verdana, helvetica, sans-serif; vertical-align: baseline; line-height: 18px; text-align: left; background-color: #ffffff; ">Now every time a connection is made to the port, the completed method will be invoked by a kernel's thread. Do you catch the difference with NIO.1? To achieve the same kind of operation with NIO.1 you would have listen for requests by doing:<span style="font-family: verdana, 'courier new'; font-size: 15px; line-height: 21px; ">&nbsp;<br /><br /></span></p><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #008080; ">&nbsp;1</span>&nbsp;selector.select(timeout);<br /><span style="color: #008080; ">&nbsp;2</span>&nbsp;<img src="http://www.blogjava.net/Images/dot.gif" alt="" /><br /><span style="color: #008080; ">&nbsp;3</span>&nbsp;<span style="color: #0000FF; ">while</span>(readyKeys.hasNext()){<br /><span style="color: #008080; ">&nbsp;4</span>&nbsp;&nbsp;SelectionKey&nbsp;key&nbsp;=&nbsp;iterators.next();<br /><span style="color: #008080; ">&nbsp;5</span>&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(key.isAcceptable()){<br /><span style="color: #008080; ">&nbsp;6</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;Do&nbsp;something&nbsp;that&nbsp;doesn't&nbsp;block<br /></span><span style="color: #008080; ">&nbsp;7</span>&nbsp;<span style="color: #008000; ">&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;because&nbsp;if&nbsp;it&nbsp;blocks,&nbsp;no&nbsp;more&nbsp;connection&nbsp;can&nbsp;be<br /></span><span style="color: #008080; ">&nbsp;8</span>&nbsp;<span style="color: #008000; ">&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;accepted&nbsp;as&nbsp;the&nbsp;selector.select(..)<br /></span><span style="color: #008080; ">&nbsp;9</span>&nbsp;<span style="color: #008000; ">&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;cannot&nbsp;be&nbsp;executed</span><span style="color: #008000; "><br /></span><span style="color: #008080; ">10</span>&nbsp;<span style="color: #008000; "></span>&nbsp;}<br /><span style="color: #008080; ">11</span>&nbsp;}<br /><span style="color: #008080; ">12</span>&nbsp;</div><div><p style="margin-top: 0.6em; margin-right: 0px; margin-bottom: 1.2em; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-width: 0px; border-style: initial; border-color: initial; border-image: initial; outline-width: 0px; outline-style: initial; outline-color: initial; vertical-align: baseline; ">With AIO, the kernel is spawning the thread for us. Where this thread coming from? This is the topic of this Tricks and Tips with AIO.<br /><br />By default, applications that do not create their own asynchronous channel group will use the default group that has an associated thread pool that is created automatically. What? The kernel will create a thread pool and manage it for me? Might be well suited for simple application, but for complex applications like the Grizzly Framework, relying on an 'external' thread pool is unthinkable as most of the time the application embedding Grizzly will configure its thread pool and pass it to Grizzly. Another reason is Grizzly has its own WorkerThread implementation that contains information about transactions (like ByteBuffer, attributes, etc.). At least the monster needs to be able to set the ThreadFactory!.<br /><br />Note that I'm not saying using the kernel's thread pool is wrong, but for Grizzly, I prefer having full control of the thread pool. So what's my solution? There is two solutions, et c'est parti:</p><h3>Fixed number of Threads (FixedThreadPool)</h3><p style="margin-top: 0.6em; margin-right: 0px; margin-bottom: 1.2em; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-width: 0px; border-style: initial; border-color: initial; border-image: initial; outline-width: 0px; outline-style: initial; outline-color: initial; vertical-align: baseline; ">An asynchronous channel group associated with a fixed thread pool of size N creates N threads that are waiting for already processed I/O events. The kernel dispatch event directly to those threads, and those thread will first complete the I/O operation (like filling a ByteBuffer during a read operation). Once ready, the thread is re-used to directly invoke the completion handler that consumes the result. When the completion handler terminates normally then the thread returns to the thread pool and wait on a next event. If the completion handler terminates due to an uncaught error or runtime exception, the thread is returned to the pool and wait for new events as well (no thread are lost). For those cases, the thread is allowed to terminate (a new event is submitted to replace it). The reason the thread is allowed to terminate is so that the thread (or thread group) uncaught exception handler is executed.<br /><br />So far so good? ....NOT. The first issue you must be aware when using fixed thread pool is if all threads "dead lock" inside a completion handler, your entire application can hangs until one thread becomes free to execute again. Hence this is critically important that the completion handler's methods complete in a timely manner so as to avoid keeping the invoking thread from dispatching to other completion handlers. If all completion handlers are blocked, any new event will be queued until one thread is 'delivered' from the lock. That can cause a really bad situation, is it? As an example, using a Future when waiting for a read operation to complete can lock you entire application:<br /><br /></p><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #008080; ">1</span>&nbsp;Future&nbsp;result&nbsp;=&nbsp;((AsynchronousSocketChannel)channel).read<br /><span style="color: #008080; ">2</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(byteBuffer,<img src="http://www.blogjava.net/Images/dot.gif" alt="" />,myCompletionHandler);<br /><span style="color: #008080; ">3</span>&nbsp;<br /><span style="color: #008080; ">4</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">try</span>{<br /><span style="color: #008080; ">5</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;count&nbsp;=&nbsp;result.get(30,&nbsp;TimeUnit.SECONDS);<br /><span style="color: #008080; ">6</span>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #0000FF; ">catch</span>&nbsp;(Throwable&nbsp;ex){<br /><span style="color: #008080; ">7</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">throw</span>&nbsp;<span style="color: #0000FF; ">new</span>&nbsp;EOFException(ex.getMessage());<br /><span style="color: #008080; ">8</span>&nbsp;&nbsp;&nbsp;&nbsp;}<br /><span style="color: #008080; ">9</span>&nbsp;</div><div><p style="margin-top: 0.6em; margin-right: 0px; margin-bottom: 1.2em; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-width: 0px; border-style: initial; border-color: initial; border-image: initial; outline-width: 0px; outline-style: initial; outline-color: initial; font-size: 12px; font-family: arial, verdana, helvetica, sans-serif; vertical-align: baseline; line-height: 18px; text-align: left; background-color: #ffffff; ">Like for OP_WRITE, I'm pretty sure nobody will ever code something like that, right? Well, some application needs to blocks until all the bytes are arrived (a Servlet Container is a good example) and if you don't paid attention, your server might hangs. Not convinced? Another example could be:<br /></p><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #008080; ">&nbsp;1</span>&nbsp;channel.write(bb,30,TimeUnit.SECONDS,db_pool,<span style="color: #0000FF; ">new</span>&nbsp;CompletionHandler()&nbsp;{<br /><span style="color: #008080; ">&nbsp;2</span>&nbsp;<br /><span style="color: #008080; ">&nbsp;3</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;completed(Integer&nbsp;byteWritten,&nbsp;DataBasePool&nbsp;attachment)&nbsp;{<br /><span style="color: #008080; ">&nbsp;4</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;Wait&nbsp;for&nbsp;a&nbsp;jdbc&nbsp;connection,&nbsp;blocking.</span><span style="color: #008000; "><br /></span><span style="color: #008080; ">&nbsp;5</span>&nbsp;<span style="color: #008000; "></span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MyDBConnection&nbsp;db_con&nbsp;=&nbsp;attachment.get();<br /><span style="color: #008080; ">&nbsp;6</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br /><span style="color: #008080; ">&nbsp;7</span>&nbsp;<br /><span style="color: #008080; ">&nbsp;8</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;failed(Throwable&nbsp;exc,&nbsp;DataBasePool&nbsp;attachment)&nbsp;{<br /><span style="color: #008080; ">&nbsp;9</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br /><span style="color: #008080; ">10</span>&nbsp;<br /><span style="color: #008080; ">11</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;cancelled(DataBasePool&nbsp;attachment)&nbsp;{<br /><span style="color: #008080; ">12</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br /><span style="color: #008080; ">13</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});<br /><span style="color: #008080; ">14</span>&nbsp;</div><br /><div><p style="margin-top: 0.6em; margin-right: 0px; margin-bottom: 1.2em; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-width: 0px; border-style: initial; border-color: initial; border-image: initial; outline-width: 0px; outline-style: initial; outline-color: initial; vertical-align: baseline; ">Again, all threads may dead lock waiting for a database connection and your application might stop working as the kernel has no thread available to dispatch and complete I/O operation.<br /><br />Grrr what's our solution? The first solution consists to carefully avoid blocking operations inside a completion handler, meaning any threads executing a kernel event must never block on something. I suspect this will be simple to achieve if you write an application from zero and you want to have a fully asynchronous application. Still, be careful and make sure you properly create enough threads. How you do that? Here is an example from Grizzly:</p></div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #008080; ">1</span>&nbsp;ThreadPoolExecutorServicePipeline&nbsp;executor&nbsp;=&nbsp;<span style="color: #0000FF; ">new</span>&nbsp;ThreadPoolExecutorServicePipeline<br /><span style="color: #008080; ">2</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(corePoolThreads,maxThreads,8192,30,TimeUnit.SECONDS);<br /><span style="color: #008080; ">3</span>&nbsp;AsynchronousChannelGroup&nbsp;asyncChannelGroup&nbsp;=&nbsp;AsynchronousChannelGroup<br /><span style="color: #008080; ">4</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.withFixedThreadPool(executor,maxThreads);<br /><span style="color: #008080; ">5</span>&nbsp;</div><div><p style="margin-top: 0.6em; margin-right: 0px; margin-bottom: 1.2em; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-width: 0px; border-style: initial; border-color: initial; border-image: initial; outline-width: 0px; outline-style: initial; outline-color: initial; vertical-align: baseline; ">The second solution is to use a cached thread pool</p><h3>Cached Thread Pool Configuration</h3><p style="margin-top: 0.6em; margin-right: 0px; margin-bottom: 1.2em; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-width: 0px; border-style: initial; border-color: initial; border-image: initial; outline-width: 0px; outline-style: initial; outline-color: initial; vertical-align: baseline; ">An asynchronous channel group associated with a cached thread pool submits events to the thread pool that simply invoke the user's completion handler. Internal kernel's I/O operations are handled by one or more internal threads that are not visible to the user application. Yup! That means you have one hidden thread pool (not configurable via the official API, but as a system property) that dispatch events to a cached thread pool, which in turn invokes completion handler (Wait! you just win a price: a thread's context switch for free ;-). Since this is a cached thread pool, the probability of suffering the hangs problem described above is lower. I'm not saying it cannot happens as you can always create cached thread pool that cannot grows infinitely (those infinite thread pool should have never existed anyway!). But at least with cached thread pool you are guarantee that the kernel will be able to complete its I/O operations (like reading bytes). Just the invocation of the completion handler might be delayed when all the threads are blocked. Note that a cached thread pool must support unbounded queueing to works properly. How you do set a cached thread pool? Here is an example from Grizzly:</p></div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #008080; ">1</span>&nbsp;ThreadPoolExecutorServicePipeline&nbsp;executor&nbsp;=&nbsp;<span style="color: #0000FF; ">new</span>&nbsp;ThreadPoolExecutorServicePipeline<br /><span style="color: #008080; ">2</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(corePoolThreads,maxCachedThreadPoolSize,8192,<br /><span style="color: #008080; ">3</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;30,TimeUnit.SECONDS);<br /><span style="color: #008080; ">4</span>&nbsp;AsynchronousChannelGroup&nbsp;asyncChannelGroup&nbsp;=&nbsp;AsynchronousChannelGroup<br /><span style="color: #008080; ">5</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.withCachedThreadPool(executor,maxCachedThreadPoolSize);<br /><span style="color: #008080; ">6</span>&nbsp;</div><br /><br /><div><h3>What about the default that ship with the kernel?</h3><p style="margin-top: 0.6em; margin-right: 0px; margin-bottom: 1.2em; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-width: 0px; border-style: initial; border-color: initial; border-image: initial; outline-width: 0px; outline-style: initial; outline-color: initial; vertical-align: baseline; "><br /><br />If you do not create your own asynchronous channel group, then the kernel's default group that has an associated thread pool will be created automatically. This thread pool is a hybrid of the above configurations. It is a cached thread pool that creates threads on demand, and it has N threads that dequeue events and dispatch directly to the application's completion handler. The value of N defaults to the number of hardware threads but may be configured by a system property. In addition to N threads, there is one additional internal thread that dequeues events and submits tasks to the thread pool to invoke completion handlers. This internal thread ensures that the system doesn't stall when all of the fixed threads are blocked, or otherwise busy, executing completion handlers.</p><h3>Conclusion</h3><p style="margin-top: 0.6em; margin-right: 0px; margin-bottom: 1.2em; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-width: 0px; border-style: initial; border-color: initial; border-image: initial; outline-width: 0px; outline-style: initial; outline-color: initial; vertical-align: baseline; ">If you have one thing to learn from this part I is independently of which thread pool you decide to use (default, your own cached or fixed), make sure you at least limit blocking operations. This is specially true when a fixed thread pool is used as it may hangs your entire application as the kernel is running out of available threads. The situation can also occurs with cached thread pool, but at least the kernel can still execute the I/O operations.</p></div><br /><br /><br /><p>&nbsp;</p></div><p>&nbsp;</p></div><p>&nbsp;</p></div><img src ="http://www.blogjava.net/cooperzh/aggbug/367000.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/cooperzh/" target="_blank">cooperzh</a> 2011-12-22 12:01 <a href="http://www.blogjava.net/cooperzh/archive/2011/12/22/367000.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>NIO trick and trap NIO的技巧与陷阱</title><link>http://www.blogjava.net/cooperzh/archive/2011/12/20/366884.html</link><dc:creator>cooperzh</dc:creator><author>cooperzh</author><pubDate>Tue, 20 Dec 2011 12:41:00 GMT</pubDate><guid>http://www.blogjava.net/cooperzh/archive/2011/12/20/366884.html</guid><description><![CDATA[<div><span style="font-size: 11px; ">出处：<a title="http://www.blogjava.net/killme2008/archive/2011/06/30/353422.html" href="http://www.blogjava.net/killme2008/archive/2011/06/30/353422.html">http://www.blogjava.net/killme2008/archive/2011/06/30/353422.html</a><br /><br />IO划分为两个阶段：</span></div><div><span style="font-size: 11px;">1 等待数据就绪</span></div><div><span style="font-size: 11px;">2 从内核缓冲区copy到进程缓冲区（从socket通过socketChannel复制到ByteBuffer）</span></div><div><span style="font-size: 11px;"><br /></span></div><div><span style="font-size: 11px;">non-direct ByteBuffer: HeapByteBuffer，创建开销小</span></div><div><span style="font-size: 11px;">direct ByteBuffer：通过操作系统native代码，创建开销大</span></div><div><span style="font-size: 11px;"><br /></span></div><div><span style="font-size: 11px;">基于block的传输通常比基于流的传输更高效</span></div><div><span style="font-size: 11px; "><br /></span></div><div><span style="font-size: 11px;">使用NIO做网络编程容易，但离散的事件驱动模型编程困难，而且陷阱重重</span></div><div><span style="font-size: 11px;"><br /></span></div><div style="background-color: #cccccc; "><span style="font-size: 11px; "><strong>Reactor模式：经典的NIO网络框架</strong></span></div><div><span style="font-size: 11px;">核心组件：</span></div><div><span style="font-size: 11px;">1 Synchronous Event Demultiplexer : Event loop + 事件分离</span></div><div><span style="font-size: 11px;">2 Dispatcher：事件派发，可以多线程</span></div><div><span style="font-size: 11px;">3 Request Handler：事件处理，业务代码</span></div><div><span style="font-size: 11px;"><br /></span></div><div><span style="font-size: 11px;"><br /></span></div><div><span style="font-size: 11px;">理想的NIO框架：</span></div><div><span style="font-size: 11px;">1 优雅地隔离IO代码和业务代码</span></div><div><span style="font-size: 11px;">2 易于扩展</span></div><div><span style="font-size: 11px;">3 易于配置，包括框架自身参数和协议参数</span></div><div><span style="font-size: 11px;">4 提供良好的codec框架，方便marshall/unmarshall</span></div><div><span style="font-size: 11px;">5 透明性，内置良好的日志记录和数据统计</span></div><div><span style="font-size: 11px;">6 高性能</span></div><div><span style="font-size: 11px;"><br /></span></div><div><span style="font-size: 11px;">NIO框架性能的关键因素</span></div><div><span style="font-size: 11px;">1 数据的copy</span></div><div><span style="font-size: 11px;">2 上下文切换(context switch)</span></div><div><span style="font-size: 11px;">3 内存管理</span></div><div><span style="font-size: 11px;">4 TCP选项，高级IO函数</span></div><div><span style="font-size: 11px;">5 框架设计</span></div><div><span style="font-size: 11px;"><br /></span></div><div><span style="font-size: 11px;">减少数据copy：</span></div><div><span style="font-size: 11px;">ByteBuffer的选择</span></div><div><span style="font-size: 11px;">View ByteBuffer</span></div><div><span style="font-size: 11px;">FileChannel.transferTo/transferFrom</span></div><div><span style="font-size: 11px;">FileChannel.map/MappedByteBuffer</span></div><div><span style="font-size: 11px;"><br /></span></div><div><span style="font-size: 11px;">ByteBuffer的选择：</span></div><div><span style="font-size: 11px;">不知道用哪种buffer时，用Non-Direct</span></div><div><span style="font-size: 11px;">没有参与IO操作，用Non-Direct</span></div><div><span style="font-size: 11px;">中小规模应用(&lt;1K并发连接)，用Non-Direct</span></div><div><span style="font-size: 11px;">长生命周期，较大的缓冲区，用Direct</span></div><div><span style="font-size: 11px;">测试证明Direct比Non-Direct更快，用Direct</span></div><div><span style="font-size: 11px;">进程间数据共享(JNI)，用Direct</span></div><div><span style="font-size: 11px;">一个Buffer发给多个Client，考虑使用view ByteBuffer共享数据，buffer.slice()</span></div><div><span style="font-size: 11px;"><br /></span></div><div><span style="font-size: 11px;"><br /></span></div><div><span style="font-size: 11px;">HeapByteBuffer缓存</span></div><div><span style="font-size: 11px;"><br /></span></div><div><span style="font-size: 11px;">使用ByteBuffer.slice()创建view ByteBuffer：</span></div><div><span style="font-size: 11px;">ByteBuffer buffer2 = buffer1.slice()；</span></div><div><span style="font-size: 11px;">则buffer2的内容和buffer1的从position到limit的数据内容完全共享</span></div><div><span style="font-size: 11px;">但是buffer2的position，limit是独立于buffer1的</span></div><div><span style="font-size: 11px;"><br /></span></div><div><span style="font-size: 11px;">传输文件的传统方式：</span></div><div><span style="font-size: 11px;">byte[] buf = new byte[8192];</span></div><div><span style="font-size: 11px;">while(in.read(buf)&gt;0){</span></div><div><span style="font-size: 11px;">&nbsp; &nbsp; out.write(buf);</span></div><div><span style="font-size: 11px;">}</span></div><div><span style="font-size: 11px;">使用NIO后：</span></div><div><span style="font-size: 11px;">FileChannel in = ...</span></div><div><span style="font-size: 11px;">WriteableByteChannel out = ...</span></div><div><span style="font-size: 11px;">in.transferTo(0,fsize,out);</span></div><div><span style="font-size: 11px;">性能会有60%的提升</span></div><div><span style="font-size: 11px;"><br /></span></div><div><span style="font-size: 11px;">FileChannel.map</span></div><div><span style="font-size: 11px;">将文件映射为内存区域&#8212;&#8212;MappedByteBuffer</span></div><div><span style="font-size: 11px;">提供快速的文件随机读写能力</span></div><div><span style="font-size: 11px;">平台相关</span></div><div><span style="font-size: 11px;">适合大文件，只读型操作，如大文件的MD5校验等</span></div><div><span style="font-size: 11px;">没有unmap方法，什么时候被回收取决于GC</span></div><div><span style="font-size: 11px;"><br /></span></div><div><span style="font-size: 11px;">减少上下文切换</span></div><div><span style="font-size: 11px;">时间缓存</span></div><div><span style="font-size: 11px;">Selector.wakeup</span></div><div><span style="font-size: 11px;">提高IO读写效率</span></div><div><span style="font-size: 11px;">线程模型</span></div><div><span style="font-size: 11px;"><br /></span></div><div><span style="font-size: 11px;">时间缓存：</span></div><div><span style="font-size: 11px;">1网络服务器通常需要频繁获取系统时间：定时器，协议时间戳，缓存过期等</span></div><div><span style="font-size: 11px;">2 System.currentTimeMillis</span></div><div><span style="font-size: 11px;">&nbsp; &nbsp;a linux调用gettimeofday需要切换到内核态</span></div><div><span style="font-size: 11px;">&nbsp; &nbsp;b 普通机器上，1000万次调用需要12秒，平均一次1.3毫秒</span></div><div><span style="font-size: 11px;">&nbsp; &nbsp;c 大部分应用不需要特别高的精度</span></div><div><span style="font-size: 11px;">3 SystemTimer.currentTimeMillis（自己创建）</span></div><div><span style="font-size: 11px;">&nbsp; &nbsp;a 独立线程定期更新时间缓存</span></div><div><span style="font-size: 11px;">&nbsp; &nbsp;b currentTimeMillis直接返回缓存值</span></div><div><span style="font-size: 11px;">&nbsp; &nbsp;c 精度取决于定期间隔</span></div><div><span style="font-size: 11px;">&nbsp; &nbsp;d 1000万次调用降低到59毫秒</span></div><div><span style="font-size: 11px;"><br /></span></div><div><span style="font-size: 11px;"><br /></span></div><div><span style="font-size: 11px;">Selector.wakeup() 主要作用：</span></div><div><span style="font-size: 11px;">解除阻塞在Selector.select()上的线程，立即返回</span></div><div><span style="font-size: 11px;">两次成功的select()之间多次调用wakeup等价于一次调用</span></div><div><span style="font-size: 11px;">如果当前没有阻塞在select()上，则本次wakeup将作用在下次select()上</span></div><div><span style="font-size: 11px;">什么时候wakeup() ？</span></div><div><span style="font-size: 11px;">注册了新的Channel或者事件</span></div><div><span style="font-size: 11px;">Channel关闭，取消注册</span></div><div><span style="font-size: 11px;">优先级更高的事件触发（如定时器事件），希望及时处理</span></div><div><span style="font-size: 11px;"><br /></span></div><div><span style="font-size: 11px;">wakeup的原理：</span></div><div><span style="font-size: 11px;">1 linux上利用pipe调用创建一个管道</span></div><div><span style="font-size: 11px;">2 windows上是一个loopback的tcp连接，因为win32的管道无法加入select的fd set</span></div><div><span style="font-size: 11px;">3 将管道或者tcp连接加入selected fd set</span></div><div><span style="font-size: 11px;">4 wakeup向管道或者连接写入一个字节</span></div><div><span style="font-size: 11px;">5 阻塞的select()因为有IO时间就绪，立即返回</span></div><div><span style="font-size: 11px;">可见wakeup的调用开销不可忽视</span></div><div><span style="font-size: 11px;"><br /></span></div><div><span style="font-size: 11px;">减少wakeup调用：</span></div><div><span style="font-size: 11px;">1 仅在有需要时才调用。如往连接发送数据，通常是缓存在一个消息队列，当且仅当队列为空时注册write并wakeup</span></div><div><span style="font-size: 11px;">booleanneedsWakeup=false;</span></div><div><span style="font-size: 11px;">synchronized(queue){</span></div><div><span style="font-size: 11px;">&nbsp; &nbsp; if(queue.isEmpty()) &nbsp;needsWakeup=true;</span></div><div><span style="font-size: 11px;">&nbsp; &nbsp; queue.add(session);</span></div><div><span style="font-size: 11px;">}</span></div><div><span style="font-size: 11px;">if(needsWakeup){</span></div><div><span style="font-size: 11px;">&nbsp; &nbsp; registerOPWrite();</span></div><div><span style="font-size: 11px;">&nbsp; &nbsp; selector.wakeup();</span></div><div><span style="font-size: 11px;">}</span></div><div><span style="font-size: 11px;">2 记录调用状态，避免重复调用，例如Netty的优化</span></div><div><span style="font-size: 11px;"><br /></span></div><div><span style="font-size: 11px;">读到或者写入0个字节：</span></div><div><span style="font-size: 11px;">不代表连接关闭</span></div><div><span style="font-size: 11px;">高负载或者慢速网络下很常见的情况</span></div><div><span style="font-size: 11px;">通常的处理方法是返回并继续注册read/write，等待下次处理，缺点是系统调用开销和线程切换开销</span></div><div><span style="font-size: 11px;">其他解决办法：循环一定次数写入（如Mina）或者yield一定次数</span></div><div><span style="font-size: 11px;">启用临时选择器Temporary Selector在当前线程注册并poll，例如Girzzy中</span></div><div><span style="font-size: 11px;"><br /></span></div><div><span style="font-size: 11px;">在当前线程写入：</span></div><div><span style="font-size: 11px;">当发送缓冲队列为空的时候，可以直接往channel写数据，而不是放入缓冲队列，interest了write等待IO线程写入，可以提高发送效率</span></div><div><span style="font-size: 11px;">优点是可以减少系统调用和线程切换</span></div><div><span style="font-size: 11px;">缺点是当前线程中断会引起channel关闭</span></div><div><span style="font-size: 11px;"><br /></span></div><div><span style="font-size: 11px;">线程模型</span></div><div><span style="font-size: 11px;">selector的三个主要事件：read，write，accept，都可以运行在不同的线程上</span></div><div><span style="font-size: 11px;"><br /></span></div><div><span style="font-size: 11px;">通常Reactor实现为一个线程，内部维护一个selector</span></div><div><span style="font-size: 11px;"><br /></span></div><div><span style="font-size: 11px;">1 Boss Thread + worker Thread</span></div><div><span style="font-size: 11px;">&nbsp; &nbsp;boss thread处理accept，connect</span></div><div><span style="font-size: 11px;">&nbsp; &nbsp;worker thread处理read，write</span></div><div><span style="font-size: 11px;"><br /></span></div><div><span style="font-size: 11px;">Reactor线程数目：</span></div><div><span style="font-size: 11px;">1 Netty 1 + 2 * cpu</span></div><div><span style="font-size: 11px;">2 Mina 1 + cpu + 1</span></div><div><span style="font-size: 11px;">3 Grizzly 1 + 1</span></div><div><span style="font-size: 11px;"><br /></span></div><div><span style="font-size: 11px;">常见线程模型：</span></div><div><span style="font-size: 11px;">1 read和accept都运行在reactor线程上</span></div><div><span style="font-size: 11px;">2 accept运行在reactor线程上，read运行在单独线程</span></div><div><span style="font-size: 11px;">3 read和accept都运行在单独线程</span></div><div><span style="font-size: 11px;">4 read运行在reactor线程上，accept运行在单独线程</span></div><div><span style="font-size: 11px;"><br /></span></div><div><span style="font-size: 11px;">选择适当的线程模型：</span></div><div><span style="font-size: 11px;">类echo应用，unmashall和业务处理的开销非常低，选择模型1</span></div><div><span style="font-size: 11px;">模型2，模型3，模型4的accept处理开销很低</span></div><div><span style="font-size: 11px;">最佳选择：模型2。unmashall一般是cpu-bound，而业务逻辑代码一般比较耗时，不要在reactor线程处理</span></div><div><span style="font-size: 11px;"><br /></span></div><div><span style="font-size: 11px;">内存管理</span></div><div><span style="font-size: 11px;">1 java能做的事情非常有限</span></div><div><span style="font-size: 11px;">2 缓冲区的管理</span></div><div><span style="font-size: 11px;">&nbsp; &nbsp;a 池化。ThreadLocal cache，环形缓冲区</span></div><div><span style="font-size: 11px;">&nbsp; &nbsp;b 扩展。putString,getString等高级API，缓冲区自动扩展和伸缩，处理不定长度字节</span></div><div><span style="font-size: 11px;">&nbsp; &nbsp;c 字节顺序。跨语言通讯需要注意，默认字节顺序Big-Endian，java的IO库和class文件</span></div><div><span style="font-size: 11px;"><br /></span></div><div><span style="font-size: 11px;">数据结构的选择</span></div><div><span style="font-size: 11px;">1 使用简单的数据结构：链表，队列，数组，散列表</span></div><div><span style="font-size: 11px;">2 使用j.u.c框架引入的并发集合类，lock-free，spin lock</span></div><div><span style="font-size: 11px;">3 任何数据结构都要注意容量限制，OutOfMemoryError</span></div><div><span style="font-size: 11px;">4 适当选择数据结构的初始容量，降低GC带来的影响</span></div><div><span style="font-size: 11px;"><br /></span></div><div><span style="font-size: 11px;">定时器的实现<br />1 定时器在网络程序中频繁使用<br />&nbsp; &nbsp; a 周期事件的触发<br />&nbsp; &nbsp; b 异步超时的通知和移除<br />&nbsp; &nbsp; c 延迟事件的触发<br />2 三个时间复杂度<br /></span>&nbsp; &nbsp; a 插入定时器<br />&nbsp; &nbsp; b 删除定时器<br />&nbsp; &nbsp; c PerTickBookkeeping，一次tick内系统需要执行的操作<br /><span style="font-size: 11px; ">3 Tick的方式<br /></span>&nbsp; &nbsp; Selector.select(timeout)<br />&nbsp; &nbsp; Thread.sleep(timeout)<br /><br /><span style="font-size: 11px; ">定时器的实现：链表<br />将定时器组织成链表结构<br />插入定时器，加入链表尾部<br />删除定时器<br /></span><div>PerTickBookkeeping，遍历链表查找expire事件</div><span style="font-size: 11px; "><br /></span><div>定时器的实现：排序链表</div><span style="font-size: 11px; ">将定时器组织成有序链表结构，按照expire截止时间升序排序<br />插入定时器，找到合适的位置插入<br />删除定时器<br /></span><div>PerTickBookkeeping，直接从表头找起</div><span style="font-size: 11px; "><br /></span><div>定时器的实现：优先队列</div><span style="font-size: 11px; ">将定时器组织成优先队列，按照expire截止时间作为优先级，优先队列一般采用最小堆实现<br />插入定时器<br />删除定时器<br /></span><div>PerTickBookkeeping，直接取root判断</div><span style="font-size: 11px; "><br /></span><div>定时器的实现：Hash wheel timer</div><span style="font-size: 11px; ">将定时器组织成时间轮<br />指针按照一定周期旋转，一个tick跳动一个槽位<br />定时器根据延时时间和当前指针位置插入到特定槽位<br /></span><div>插入定时器<br />删除定时器</div><div>PerTickBookkeeping</div><div>槽位和tick决定了精度和延时<br /><br /><div>定时器的实现：Hierarchical Timing</div></div><span style="font-size: 11px; ">Hours Wheel，Minutes Wheel，Seconds Wheel<br /><br />连接IDLE的判断<br />1 连接处于IDLE状态：一段时间没有IO读写事件发生<br />2 实现方式：<br />&nbsp; &nbsp; a 每次IO读写都记录IO读和写的时间戳<br />&nbsp; &nbsp; b 定时扫描所有连接，判断当前时间和上一次读或写的时间差是否超过设定阀值，超过即认为连接处于IDLE状态，通知业务处理器<br /></span>&nbsp; &nbsp;c 定时的方式：基于select(timeout)或者定时器。Mina：select(timeout);Netty:HashWheelTimer<br /><span style="font-size: 11px; "><br />合理设置TCP/IP选项，有时会起到显著效果，需要根据应用类型、协议设计、网络环境、OS平台等因素做考量，以测试结果为准<br /><br />Socket缓冲区设置选项：SO_RCVBUF 和 SO_SNDBUF<br />Socket.setReceiveBufferSize/setSendBufferSize 仅仅是对底层平台的提示，是否有效取决于底层平台。因此get返回的不是真实的结果。<br />设置原则：<br />1 以太网上，4k通常是不够的，增加到16k，吞吐量增加了40%<br />2 Socket缓冲区大小至少应该是连接的MSS的三倍，MSS=MTU+40，一般</span>以太网卡的MTU=1500字节。<br />&nbsp;&nbsp;&nbsp;&nbsp;MSS：最大分段大小<br />&nbsp; &nbsp; MTU：最大传输单元<br /><span style="font-size: 11px; ">3 send buffer最好与对端的receive buffer尺寸一致<br />4 对于一次性发送大量数据的应用，增加缓冲区到48k、64k可能是唯一最有效的提高性能的方式。<br />&nbsp; &nbsp; 为了最大化性能，</span>send buffer至少要跟BDP(带宽延迟乘积)一样大。<span style="font-size: 11px; "><br /></span><span style="font-size: 11px; ">5 同样，对于大量接收数据的应用，提高接收缓冲区，能减少发送端的阻塞<br />6 如果应用既发送大量数据，又接收大量数据，则</span>send buffer和<span class="Apple-style-span" style="font-size: 11px; ">receive buffer应该同时增加<br /></span>7 如果设置的ServerSocket的<span class="Apple-style-span" style="font-size: 11px; ">receive buffer超过RFC1323定义的64k，那么必须在绑定端口前设置，以后accept产生的socket将继承这一设置<br /></span>8 无论缓冲区大小多少，你都应该尽可能地帮助TCP至少以那样大小的块写入<br /><span style="font-size: 11px; "><br /></span><div>BDP(带宽延迟乘积)</div><span style="font-size: 11px; ">为了优化TCP吞吐量，发送端应该发送足够的数据包以填满发送端和接收端之间的逻辑通道<br />BDP = 带宽 * RTT<br /><br />Nagle算法：SO_TCPNODELAY<br />通过将缓冲区内的小包自动相连组成大包，阻止发送大量小包阻塞网络，提高网络应用效率对于实时性要求较高的应用(telnet、网游)，需要关闭此算法<br />Socket.setTcpNoDelay(true) 关闭算法<br /><div style="display: inline-block; "><div>Socket.setTcpNoDelay(false)&nbsp;</div></div>打开算法，默认<br /><br />SO_LINGER选项，控制socket关闭后的行为<br />Socket.setSoLinger(boolean linger,int timeout)<br />1&nbsp;</span>linger=false，timeout=-1<br />当socket主动close，调用的线程会马上返回，不会阻塞，然后进入CLOSING状态，残留在缓冲区中的数据将继续发送给对端，并且与对端进行FIN-ACK协议交换，最后进入TIME_WAIT状态<br /><div><span style="font-size: 11px; ">2&nbsp;</span>linger=true，timeout&gt;0</div><span style="font-size: 11px; ">调用close的线程将阻塞，发生两种可能的情况：一是剩余的数据继续发送，进行关闭协议交换，二是超时过期，剩余数据将被删除，</span>进行FIN-ACK协议交换<span style="font-size: 11px; "><br /></span><div><span style="font-size: 11px; ">3&nbsp;</span>linger=true，timeout=0</div><span style="font-size: 11px; ">进行所谓&#8220;hard-close&#8221;，任何剩余的数据将被丢弃，并且FIN-ACK交换也不会发生，替代产生RST，让对端抛出&#8220;connection reset&#8221;的SocketException<br />4 慎重使用此选项，TIME_WAIT状态的价值：<br />&nbsp;&nbsp;&nbsp;&nbsp;可靠实现TCP连接终止<br />&nbsp;&nbsp;&nbsp;&nbsp;允许</span>老的分节在网络中流失，防止发给新的连接<br />&nbsp; &nbsp;持续时间=2*MSL（MSL为最大分节生命周期，一般为30秒到2分钟）<span style="font-size: 11px; "><br /></span><span style="font-size: 11px; "><br />SO_REUSEADDR：重用端口<br />Socket.setReuseAddress(boolean) 默认false<br />适用场景：<br />1 当一个使用本地地址和端口的socket1处于TIME_WAIT状态时，你启动的socket2要占用该地址和端口，就要用到此选项<br />2&nbsp;</span>SO_REUSEADDR允许同一端口上启动一个服务的多个实例（多个进程），但每个实例绑定的地址是不能相同的<br /><span style="font-size: 11px; ">3</span>&nbsp;SO_REUSEADDR允许完全相同的地址和端口的重复绑定。但这只用于UDP的多播，不适用TCP<br /><br /><span style="font-size: 11px; ">SO_REUSEPORT<br />listen做四元组，多进程同一地址同一端口做accept，适合大量短连接的web server<br />Freebsd独有<br /><br />其他选项：<br />Socket.setPerformancePreferences(connectionTime, latency, bandwidth) 设置连接时间、延迟、带宽的相对重要性<br />Socket.setKeepAlive(boolean) 这是TCP层的keep-alive概念，非HTTP协议的。用于TCP连接保活，默认间隔2小时，建议在应用层做心跳<br />Socket.sendUrgentData(data) 带外数据<br /></span><span style="font-size: 11px; "><br /><br />技巧：<br />1 读写公平<br />&nbsp; &nbsp; Mina限制一次写入的字节数不超过最大的读缓冲区的1.5倍<br />2 针对FileChannel.transferTo的bug<br /></span>&nbsp; &nbsp; Mina判断异常，如果是temporarily unavailable的IOException，则认为传输字节数为0<br /><span style="font-size: 11px; ">3 发送消息，通常是放入一个缓冲区队列注册write，等待IO线程去写<br />&nbsp; &nbsp; 线程切换，系统调用<br />&nbsp; &nbsp; 如果队列为空，直接在当前线程channel.write，隐患是当前线程的中断会引起连接关闭<br />4 事件处理优先级<br />&nbsp; &nbsp; ACE框架推荐：accept &gt; write &gt; read (推荐)<br />&nbsp; &nbsp; &nbsp;Mina 和 Netty：read &gt; write<br />5 处理事件注册的顺序<br /></span>&nbsp; &nbsp; 在select()之前<br />&nbsp; &nbsp; 在select()之后，处理wakeup竞争条件<br /><br />Java Socket实现在不同平台上的差异<br />由于各种OS平台的socket实现不尽相同，都会影响到socket的实现<br />需要考虑性能和健壮性<br />&nbsp; &nbsp;&nbsp;<span style="font-size: 11px; "><br />&nbsp;<br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /></span></div><div><span class="Apple-style-span" style="font-size: 11px; "><br /></span></div><div><span style="font-size: 11px;"><br /></span></div><div style="font-family: Tahoma; font-size: 11px; "></div><img src ="http://www.blogjava.net/cooperzh/aggbug/366884.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/cooperzh/" target="_blank">cooperzh</a> 2011-12-20 20:41 <a href="http://www.blogjava.net/cooperzh/archive/2011/12/20/366884.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>