说说MaxTenuringThreshold这个参数

MaxTenuringThreshold这个参数用于控制对象能经历多少次Minor GC才晋升到旧生代,默认值是15,那是不是意味着对象要经历15次minor gc才晋升到旧生代呢,来看下面的一个例子。

MaxTenuringThreshold这个参数用于控制对象能经历多少次Minor GC才晋升到旧生代,默认值是15,那是不是意味着对象要经历15次minor gc才晋升到旧生代呢,来看下面的一个例子。

public class GCTenuringThreshold{
public static void main(String[] args) throws Exception{
GCMemoryObject object1=new GCMemoryObject(2);
GCMemoryObject object2=new GCMemoryObject(8);
GCMemoryObject object3=new GCMemoryObject(8);
GCMemoryObject object4=new GCMemoryObject(8);
object2=null;
object3=null;
GCMemoryObject object5=new GCMemoryObject(8);
Thread.sleep(4000);
object2=new GCMemoryObject(8);
object3=new GCMemoryObject(8);
object2=null;
object3=null;
object5=null;
GCMemoryObject object6=new GCMemoryObject(8);
Thread.sleep(5000);
}
}
class GCMemoryObject{
private byte[] bytes=null;
public GCMemoryObject(int multi){
bytes=new byte[1024*256*multi];
}
}

以-Xms20M –Xmx20M –Xmn10M –XX:+UseSerialGC参数执行以上代码,通过jstat -gcutil [pid] 1000 10的方式查看执行效果,很惊讶执行结果竟然是在第二次minor GC的时候object1就被晋升到old中了,而可以肯定的是这个时候to space空间是充足的,也就是说并不是在to space空间充足的情况下,对象一定要经历MaxTenuringThreshold次才会晋升到old,那具体规则到底是怎么样的呢,翻看Hotspot 6 update 21中SerialGC的实现,可以看到在每次minor GC后,会对这个存活周期的阈值做计算,计算的代码如下:

size_t desired_survivor_size = (size_t)((((double) survivor_capacity)*TargetSurvivorRatio)/100);
size_t total = 0;
int age = 1;
assert(sizes[0] == 0, "no objects with age zero should be recorded");
while (age < table_size) { total += sizes[age]; // check if including objects of age 'age' made us pass the desired // size, if so 'age' is the new threshold if (total > desired_survivor_size) break;
age++;
}
int result = age < MaxTenuringThreshold ? age : MaxTenuringThreshold;

其中desired_survivor_size是指survivor space/2,从上面的代码可看出,在计算存活周期这个阈值时,hotspot会遍历所有age的table,并对其所占用的大小进行累积,当累积的大小超过了survivor space的一半时,则以这个age作为新的存活周期阈值,最后取age和MaxTenuringThreshold中更小的一个值。

按照这样的规则,上面的运行效果就可验证了,第一次minor gc的时候存活周期的阈值为MaxTenuringThreshold,minor gc结束后计算出新的阈值为1,在第二次minor gc时object 1的age已经是1了,因此object1被晋升到了旧生代。

这个规则对于Serial GC以及ParNew GC(但对于开启了UseAdaptiveSizePolicy的ParNew GC而言也无效,默认是不开启的)均有效,对于PS(Parallel Scavenge) GC而言,在默认的情况下第一次以InitialTenuringThreshold(默认为7)为准,之后在每次minor GC后均会动态计算,规则比上面的复杂,后续再另写一篇blog来说,在设置-XX:-UseAdaptiveSizePolicy后,则以MaxTenuringThrehsold为准,并且不会重新计算,会是恒定值。

如希望跟踪每次minor GC后新的存活周期的阈值,可在启动参数上增加:-XX:+PrintTenuringDistribution,输出的信息中的:
Desired survivor size 1048576 bytes, new threshold 7 (max 15)
new threshold 7即标识新的存活周期的阈值为7。

《分布式Java应用:基础与实践》样章、代码、纠错、补充

《分布式Java应用:基础与实践》一书中会存在一些错误的地方,以及一些尚未深入讲解的部分,在这篇文章里会提供纠错的信息以及补充的内容的文章的链接,关于书中错误的部分,还请各位海涵和帮助指正。

样章请从此下载:
http://bluedavy.me/book/booksample.pdf

随书的代码请从此处下载:
http://bluedavy.me/book/source.zip

《分布式Java应用:基础与实践》一书中会存在一些错误的地方,以及一些尚未深入讲解的部分,在这篇文章里会提供纠错的信息以及补充的内容的文章的链接,关于书中错误的部分,还请各位海涵和帮助指正。

样章请从此下载:
http://bluedavy.me/book/booksample.pdf

随书的代码请从此处下载:
http://bluedavy.me/book/source.zip

===============纠错===============

  • 第257页 — 感谢@偶系阿萌 的反馈
    “当节点较少时,有可能会出现这些机器节点是均匀分布的现象”
    应修改为:
    “当节点较少时,有可能会出现这些机器节点是不均匀分布的现象”

  • 第41页 — 感谢dongtalk的反馈
    “各厂商在实现JDK时通常会将符合Java语言规范的源代码编译为class文件的编译器”
    应修改为:
    “各厂商在实现JDK时通常会提供将符合Java语言规范的源代码编译为class文件的编译器”

  • 第46页 — 感谢yeshucheng的反馈
    图3.4中的“Booksrap ClassLoader”应为“BootStrap ClassLoader”

  • 第68页
    图3.13用来说明Sun hotspot中可用的GC,其中旧生代可用的GC的图有错误的地方,准确来说,在Sun Hotspot V 1.6.0中并行GC应该有两种:Parallel Mark Sweep和Parallel Compacting。

  • 第71页
    “只有经历过几次Minor GC仍然存活的对象,才放入旧生代中,这个在Minor GC中存活的次数在串行和ParNew方式时可通过-XX:MaxTenuringThreshold来设置,在Parallel Scavenge时则由Hotspot根据运行状况来决定。”
    这句话准确的应为:
    “只有经历过几次Minor GC仍然存活的对象,才放入旧生代中,这个在Minor GC中存活的最大次数在串行和ParNew方式时可通过-XX:MaxTenuringThreshold来设置,但并不代表对象一定会存活MaxTenuringThreshold次才会晋升到旧生代,串行和ParNew采用一个规则在每次Minor GC后计算可存活的次数,规则为累积每个age的对象所占的内存,一直计算到占用大小超过survivor space一半的age,如计算了所有的age,均未超过则以MaxTenuringThreshold为准,否则则以age为准,在Parallel Scavenge时默认情况下由Hotspot根据运行状况来决定。”。

  • 第73页
    “并行GC在基于SurvivorRatio值划分eden space和两块survivor space的方式上和串行GC一样。”
    修正为
    “默认情况下并行GC在基于SurvivorRatio值划分eden space和两块survivor space的方式上和串行GC一样,在开启-XX:+UseAdaptiveSizePolicy后则为每次Minor GC后动态计算eden、to的大小。”

  • 第73页
    “当在Eden Space上分配内存时Eden Space空间不足,JVM即触发Minor GC的执行,也可在程序中通过System.gc的方式(可通过在启动参数中增加-XX:+DisableExplicitGC来避免程序中调用System.gc触发GC)来触发。”
    修正为
    “当在Eden Space上分配内存时Eden Space空间不足,JVM即触发Minor GC的执行,当旧生代采用并行GC时,也可在程序中通过System.gc的方式(可通过在启动参数中增加-XX:+DisableExplicitGC来避免程序中调用System.gc触发GC)来触发。”

  • 第77页 — 感谢dongtalk的反馈
    “首先将代空间划分为并行线程个数的区域(regions)”
    应修改为
    “首先将旧生代空间划分为并行线程个数的区域(regions)”

  • 第77页
    并行应为并行Compacting,书中没有介绍Parallel Mark Sweep,在后续的blog中会进行完善;

  • 第78页
    这句话是错误的:“并行是server级别机器(非32位Windows)上默认采用的GC方式,也可通过-XX:+UseParallelGC或-XX:+UseParallelOldGC来强制指定。”
    修正为
    “并行是server级别机器(非32位Windows)上默认采用的GC方式,可通过-XX:+UseParallelGC来指定使用Parallel Mark Sweep,通过-XX:+UseParallelOldGC来指定使用Parallel Compacting。”;

  • 第80页
    “CMS GC触发的条件为旧生代已使用的空间达到设定的CMSInitiatingOccupancyFraction百分比,例如默认CMSInitiatingOccupancyFraction为68%,如旧生代空间为1 000MB,那么当旧生代已使用的空间达到680MB时,CMS GC即开始执行;”
    修正为
    ”CMS GC触发的条件为旧生代已使用的空间达到设定的CMSInitiatingOccupancyFraction百分比或持久代已使用的空间达到设定的CMSInitiatingPermOccupancyFraction百分比,例如在JDK 6.0中默认值为92,如旧生代空间为1000MB,那么当旧生代已使用的空间达到920MB时,CMS GC即开始执行;“

  • 第80页
    ”持久代的GC也可采用CMS方式,方式为设置以下参数:-XX:+CMSPermGenSweepingEnabled -XX:+CMSClassUnloadingEnabled。“
    修正为
    ”持久代的GC也可采用CMS方式,方式为设置此参数:-XX:+CMSClassUnloadingEnabled。“;

  • 第87页
    表3.1中:Server模式下旧生代和持久代GC方式准确的说应为Parallel Mark Sweep;

  • 第87页
    表3.2中:”-XX:+UseParallelGC 并行回收GC 并行GC“准确的说应为:”-XX:+UseParallelGC 并行回收GC Parallel Mark Sweep GC“;

  • 第87页
    表3.2中:“-XX:+UseParallelOldGC 并行回收GC 并行GC”准确的说应为:“-XX:+UseParallelOldGC 并行回收GC 并行Compacting GC”;

  • 第99页 — 感谢liang xie的反馈
    “如果要分析jvm堆dumap文件”
    应为:
    “如果要分析jvm堆dump文件”

  • 第112页 — 感谢liang xie的反馈
    “JDK常用package中的常用类进行分析……….,对根据需求合理地选择类会有一定的帮助”产生了重复。

  • 第113页 — 感谢libai的反馈
    ”super调用的为AbstractList的默认构造器方法….ArrayList采用的是数组的方式来存放对象“
    这段话出现了重复。

  • 第183页 — 感谢liang xie的反馈
    “在没有安装pidstat或内核版本为2.6.20以后”
    应为
    “在没有安装pidstat或内核版本为2.6.20之前”

    ===============内容补充===============
    1、说说MaxTenuringThreshold这个参数
    2、Sun JDK OOM

  • 《分布式Java应用:基础与实践》序

    分布式Java应用需要开发人员掌握较多的知识点,通常分布式Java应用的场景还会对性能、可用性以及可伸缩有较高的要求,而这也就意味着开发人员需要掌握更多的知识点。我刚进淘宝的时候,曾经一直苦恼对于一个这样的分布式Java应用,我到底需要学习些什么。

    随着在淘宝工作的不断开展,我的眼前终于慢慢呈现了高性能、高可用以及可伸缩的Java应用所需知识点的全景,这张知识点的全景图现在已经演变成了本书的目录。当看到自己整理出的知识点的全景图时,很惊讶地发现其中有些知识点其实是我之前已经学习过的,但到了真正需要使用的时候有些是完全遗忘了,有些则是在使用时碰到了很多的问题,从这里我看到,当学习到的知识不去经过实践检验时,这些知识就不算真正属于自己。

    分布式Java应用需要开发人员掌握较多的知识点,通常分布式Java应用的场景还会对性能、可用性以及可伸缩有较高的要求,而这也就意味着开发人员需要掌握更多的知识点。我刚进淘宝的时候,曾经一直苦恼对于一个这样的分布式Java应用,我到底需要学习些什么。

    随着在淘宝工作的不断开展,我的眼前终于慢慢呈现了高性能、高可用以及可伸缩的Java应用所需知识点的全景,这张知识点的全景图现在已经演变成了本书的目录。当看到自己整理出的知识点的全景图时,很惊讶地发现其中有些知识点其实是我之前已经学习过的,但到了真正需要使用的时候有些是完全遗忘了,有些则是在使用时碰到了很多的问题,从这里我看到,当学习到的知识不去经过实践检验时,这些知识就不算真正属于自己。

    幸运的是,在淘宝我得到了分布式Java应用的绝佳实践机会,于是所学习到的网络通信、高性能、高并发、高可用以及可伸缩的一些知识,有机会在实践中得到验证。正是这样的机会,让我对这些知识点有了更深刻本质的理解,并能将其中的一些知识真正吸收,变为自己的经验,所以我一个很真切的体会就是:实践是最好的成长。

    经历了这段艰难的成长,自己也希望不要忘却在这个过程中的收获,胡适先生曾说:“发表是最好的记忆”(这句话也长期放在台湾技术作家侯捷老师的网站上),于是萌生了写这本书的想法,一方面想梳理自己通过实践所获得的成长,另一方面也希望与正在从事分布式Java应用的技术人员分享一些实践的心得,同时给将要或打算从事分布式Java应用的技术人员提供一些参考。

    从2008年11月确认要写这本书,到2010年5月完成这本书,历时一年半,过程可谓波折不断,前3个月的写作一帆风顺,顺利完成了第一章和第三章的撰写。

    到了2009年3月底后,由于投入到了《OSGi原理与最佳实践》一书的编写中,停顿了将近3个月的时间。

    2009年8月重新开始了这本书的撰写,在2009年10月下旬前按计划完成了第二章、第四章和第五章,但随后由于项目进入冲刺阶段、忙于校园招聘以及迁居等事情,再度停顿了本书的撰写。

    记得在刚开始写这本书的时候,周筠老师就告诉我,要坚持写,就算每天只写一点也是好的,千万不要停顿!确实如此,在停顿了两次后,就很难再找到继续写这本书的动力和激情了,得感谢徐定翔编辑在之后给我的鼓励和督促,终于在2010年3月我又开始了写作。待完成了第六章的编写后,由于剩下的第七章中的部分知识点和自己的工作联系不是非常紧密,导致了不断的拖延,这时周筠老师和徐定翔编辑给我的一个建议起到了关键作用,就是先放下第七章,先做之前完成的六章的定稿。

    回到自己熟悉的前6章,终于再次有了写作的动力,随着工作中对自己书中所涉及的知识点的不断实践,此时再回头看自己半年前甚至一年前写的初稿时,发现其中有不少的错误以及条理不够清晰的地方,于是进行了大刀阔斧的改动,实践所获得的积累此时起了巨大的推动作用,这6章定稿除了第三章以外的章节,完成得较为顺利。

    6章定稿提交后,由白爱萍编辑带领的编辑小组再次对书稿进行细致的“田间管理”,给出了非常多的修改建议,印象中几乎平均每页都至少有两到三处需要修改的地方,正是他们的认真和专业,使得定稿中很多语言上以及技术上的错误得以纠正,感谢武汉博文的编辑们。

    最后,在第三章定稿的修改过程中,得到了同事莫枢(http://rednaxelafx.javaeye.com)非常多的建议和帮助,在此非常感谢他的支持。

    全书在编写的过程中,初稿、定稿也提交给了一些业内专家帮忙评审,主要有:郑晖、霍炬、曹祺、刘力祥,他们给出的很多意见一方面纠正了书中一些技术上的错误,另一方面也让书的条理性更加顺畅,衷心感谢他们的辛勤付出。尤其郑晖老师,在承诺为本书写推荐序时,又花时间把全稿通读一遍,他的耐心和专业精神,让我感佩不已。

    书的撰写过程是如此漫长,每天晚上下班后、周末、假期,甚至过年期间,都成了写书的时间,感谢家人给我的包容、支持和理解,最要感谢的是我明年春节就将迎娶的准老婆:宗伟,感谢你忍受了我不断忘记买家里需要的各种东西,感谢你独立完成了新家的装修,更要感谢你允许我这么多的周末、假日都无法陪伴你,没有你的支持和鼓励,这本书是无法完成的。

    回顾整个编写过程,从开始编写,到提交完全部终稿,经历了15个月的时间,写作时间大概为11个月,经过这11个月对这些知识点的不断实践、回顾和总结,它们在我的脑海中刻下了深深的烙印,的确,发表是最好的记忆。

    林昊

    2010年5月20日晚于杭州家中

    《分布式Java应用:基础与实践》的封面和目录

    封面:

    目录请见全文。

    封面:

    目录如下:
    第1章 分布式Java应用 1
    1.1 基于消息方式实现系统间的通信 3
    1.1.1 基于Java自身技术实现消息方式的系统间通信 3
    1.1.2 基于开源框架实现消息方式的系统间通信 10
    1.2 基于远程调用方式实现系统间的通信 14
    1.2.1 基于Java自身技术实现远程调用方式的系统间通信 14
    1.2.2 基于开源框架实现远程调用方式的系统间通信 17
    第2章 大型分布式Java应用与SOA 23
    2.1 基于SCA实现SOA平台 26
    2.2 基于ESB实现SOA平台 29
    2.3 基于Tuscany实现SOA平台 30
    2.4 基于Mule实现SOA平台 34
    第3章 深入理解JVM 39
    3.1 Java代码的执行机制 40
    3.1.1 Java源码编译机制 41
    3.1.2 类加载机制 44
    3.1.3 类执行机制 49
    3.2 JVM内存管理 63
    3.2.1 内存空间 63
    3.2.2 内存分配 65
    3.2.3 内存回收 66
    3.2.4 JVM内存状况查看方法和分析工具 92
    3.3 JVM线程资源同步及交互机制 100
    3.3.1 线程资源同步机制 100
    3.3.2 线程交互机制 104
    3.3.3 线程状态及分析 105
    第4章 分布式应用与Sun JDK类库 111
    4.1 集合包 112
    4.1.1 ArrayList 113
    4.1.2 LinkedList 116
    4.1.3 Vector 117
    4.1.4 Stack 118
    4.1.5 HashSet 119
    4.1.6 TreeSet 120
    4.1.7 HashMap 120
    4.1.8 TreeMap 123
    4.1.9 性能测试 124
    4.1.10 小结 138
    4.2 并发包(java.util.concurrent) 138
    4.2.1 ConcurrentHashMap 139
    4.2.2 CopyOnWriteArrayList 145
    4.2.3 CopyOnWriteArraySet 149
    4.2.4 ArrayBlockingQueue 149
    4.2.5 AtomicInteger 151
    4.2.6 ThreadPoolExecutor 153
    4.2.7 Executors 157
    4.2.8 FutureTask 158
    4.2.9 Semaphore 161
    4.2.10 CountDownLatch 162
    4.2.11 CyclicBarrier 163
    4.2.12 ReentrantLock 164
    4.2.13 Condition 164
    4.2.14 ReentrantReadWriteLock 165
    4.3 序列化/反序列化 167
    4.3.1 序列化 167
    4.3.2 反序列化 170
    第5章 性能调优 173
    5.1 寻找性能瓶颈 175
    5.1.1 CPU消耗分析 175
    5.1.2 文件IO消耗分析 182
    5.1.3 网络IO消耗分析 186
    5.1.4 内存消耗分析 187
    5.1.5 程序执行慢原因分析 191
    5.2 调优 192
    5.2.1 JVM调优 192
    5.2.2 程序调优 202
    5.2.5 对于资源消耗不多,但程序执行慢的情况 214
    第6章 构建高可用的系统 227
    6.1 避免系统中出现单点 228
    6.1.1 负载均衡技术 228
    6.1.2 热备 236
    6.2 提高应用自身的可用性 238
    6.2.1 尽可能地避免故障 239
    6.2.2 及时发现故障 246
    6.2.3 及时处理故障 248
    6.2.4 访问量及数据量不断上涨的应对策略 249
    第7章 构建可伸缩的系统 251
    7.1 垂直伸缩 252
    7.1.1 支撑高访问量 252
    7.1.2 支撑大数据量 254
    7.1.3 提升计算能力 254
    7.2 水平伸缩 254
    7.2.1 支撑高访问量 254
    7.2.2 支撑大数据量 264
    7.2.3 提升计算能力 266

    Sun JDK V1.6.0 JVM GC

    本PPT目前版本仅为0.8,后续仍然会进一步完善,只介绍了在Sun Hotspot V 1.6.0中:
    1、内存结构;
    2、内存分代,如何控制代大小;
    3、可用的GC,每种GC对于参数的不同使用,例如SurvivorRatio、MaxTenuringThreshold等;每种GC不同的内存分配策略和回收策略,但不涉及具体算法是如何实现的;
    4、GC是怎么触发的,日志是什么含义;
    5、怎么使用上面的GC;
    6、GC Tuning,一个案例以及简单介绍了一些常见的GC调优的目标时的瓶颈、可采用的方法等;
    7、Sun JDK是如何实现GC的。

    本PPT目前版本仅为0.8(更新于2010-06-08),后续仍然会进一步完善,只介绍了在Sun Hotspot V 1.6.0中:
    1、内存结构;
    2、内存分代,如何控制代大小;
    3、可用的GC,每种GC对于参数的不同使用,例如SurvivorRatio、MaxTenuringThreshold等;每种GC不同的内存分配策略和回收策略,但不涉及具体算法是如何实现的;
    4、GC是怎么触发的,日志是什么含义;
    5、怎么使用上面的GC;
    6、GC Tuning,一个案例以及简单介绍了一些常见的GC调优的目标时的瓶颈、可采用的方法等;
    7、Sun JDK是如何实现GC的。

    code show: 网络访问超时优化

    在这篇blog中,我们将来探讨一个优化的案例,这个案例的背景为:在网络通信中,通常会有需求是从A发送了消息给B,然后同步或异步等待B的响应,但不可能无限期的等下去,因此会需要一个超时机制,通常在基于NIO这样的机制中,发送和接收会是异步方式,通常实现时会采用类似如下的方法,代码简单示例如下。

    在这篇blog中,我们将来探讨一个优化的案例,这个案例的背景为:在网络通信中,通常会有需求是从A发送了消息给B,然后同步或异步等待B的响应,但不可能无限期的等下去,因此会需要一个超时机制,通常在基于NIO这样的机制中,发送和接收会是异步方式,通常实现时会采用类似如下的方法,代码简单示例如下:

    invoke(){

    send(request);

    }

    onMessageReceived(){

    // 接收到响应后的处理

    }

    在不需要超时的情况下,这样就OK了,在有超时的情况下,则需要启动一个线程来扫描所有连接上(连接太多,所以不太可能每个连接就启一个线程来扫)的所有请求(每个请求的超时时间可能都不一样,有些是1s、有些是3s),这里就出现了一个问题,到底是多久扫描一次呢?简单的实现的话就可以是固定时间间隔扫描一次,这样扫描就显得有些傻了,因为其实很多请求根本就还没到超时时间,而随着连接数、请求数的增加,这里的效率就成问题了,高级点的话就演变为将最近超时的放到最顶端,类似Timer或DelayedQueue的实现,但这样的方式都存在一个问题,就是由于需要排序,所以在往queue里插入和删除对象的时候都需要加锁,这无疑会产生很大的影响,so无法接受,期望能做到的是:

    1、单线程扫描请求是否超时;

    2、在加入需要扫描的请求时无需加锁;

    3、超时扫描等到达了最近有超时的请求后才启动,否则沉默;

    4、当请求响应已回来后,可删除需要扫描的相应请求,避免在超时时间段内堆积太多扫描的请求,消耗内存。

    我们之前想到的一个方法为

    invoke(){

    addToScanQueue(request.getId(),request.getTimeout());

    send(request);

    }

    addToScanQueue{

    // 判断是否有对应request.getTimeout的queue,如果没有则创建,并往queue里塞入这次需要扫描的请求的id,超时时间,塞入后检查其超时时间和扫描线程的睡眠时间,如超时时间小于睡眠时间,则唤醒扫描线程

    }

    扫描线程{

    // 扫描所有的queue,并取出第一个对象,看看有没有超时,直到取到一个没超时的,比较所有queue中没超时的,看谁时间短,以短的为下次触发扫描的时间点

    }

    上面这个方法有效的借助了request.getTimeout种类不多的场景以及FIFO的特性,基本上可以做到插入无锁,而且扫描基本上可以等到有需要扫描的才扫描,但有个问题…就是已经有了响应的请求怎么从这里删除呢,一方面queue不好删,另一方面要删多数就要加锁了,如果转为用mark机制的话,由于大部分请求其实都不会超时的,这里会堆积很多的对象,消耗内存,纠结呀…

    有同学有兴趣尝试下改造上面的方式吗,既然是code show,感兴趣的同学可直接在回复中贴上代码,:)

    杭州程序员圆桌交流第二期视频

    本次交流在4月24日圆满完成,主题为关于JVM的那些事,撒迦@rednaxelafx给大家做了一个长达四小时的精彩分享,涵盖了javac、解释执行、c1、c2编译执行方面的知识点。

    由于视频太大,感兴趣的同学请从以下地址下载,自行观看,:),也欢迎看完后在twitter上,或在这里来进行讨论。

    本次交流在4月24日圆满完成,主题为关于JVM的那些事,撒迦@rednaxelafx给大家做了一个长达四小时的精彩分享,涵盖了javac、解释执行、c1、c2编译执行方面的知识点。

    由于视频太大,感兴趣的同学请从以下地址下载,自行观看,:),也欢迎看完后在twitter上,或在这里来进行讨论。

    ps: 撒迦同学在blog上也写了下这件事,最重要的是同时还放出了PPT,大家可以移步到此:

    http://rednaxelafx.javaeye.com/blog/656951

    (1)Part I
    (2)Part II
    (3)Part III
    (4)Part IV
    (5)Part V
    (6)Part VI

    大型应用与SOA

    摘自我那本6月份要上市的,但目前名字还没完全确定的书,由于书中涵盖的更多的为构建高性能分布式Java应用所需要的基础知识,也许改成了《通往高性能分布式Java应用之路》,摘取的这段内容主要是阐述下为什么大型应用需要SOA,以及eBay的例子。

    摘自我那本6月份要上市的,但目前名字还没完全确定的书,由于书中涵盖的更多的为构建高性能分布式Java应用所需要的基础知识,也许改成了《通往高性能分布式Java应用之路》,摘取的这段内容主要是阐述下为什么大型应用需要SOA,以及eBay的例子。

    当应用获得用户的认可后,会不断的发展,以豆瓣 为例,早期豆瓣只有书评的功能,随着大家对豆瓣的认可以及使用的用户越来越多,豆瓣发展到了今天的豆瓣社区、豆瓣读书、豆瓣电影和豆瓣音乐等功能,这四个大块的功能有各自的特色,但又有很多可公用的业务逻辑,例如用户信息、评价等,如这四个系统都维护自己的用户信息读写或评价业务逻辑,此时会造成的问题一方面是当需要修改评价逻辑时,所有系统有可能都需要修改,当需要修改用户信息的读取方式时,也是所有系统都需要修改,会相当的复杂;另一方面是每个系统上都有很多种类的业务逻辑,这就像在一个小超市中,一个人负责收银、清洁、摆货、咨询等各种各样的事情,当来超市买东西的人多了后,这个人就没有办法再负责这么多的事情了,系统也同样如此。

    第一个现象为系统多元化后带来的问题,可采用的方法为对共用逻辑的部分进行抽象,形成多个按领域划分的共用业务逻辑系统;第二个现象为系统访问量、数据量上涨后带来的典型问题,当超市的顾客不断增加时,通常超市会采取分工的方式来更好的服务顾客,同样,对于系统而言,也会采取拆分系统的方式来解决。

    在构建了共用业务逻辑系统和拆分系统后,最明显的问题就是系统之间如何进行交互,如不做控制,会出现的现象是多个系统之间出现多种多样的交互方式,Http、TCP/IP+NIO、Hessian、RMI、Webservice等;同步、异步等方式可能都会出现,这会导致开发人员在每访问一个共用业务逻辑系统或拆分出来的系统后,都有可能要学习不同的交互方式;同时也会造成各开发团队重复造轮子,提供不同交互方式用的框架,这对于应用的性能、可用性而言也带来了极大的挑战。

    对于上面的问题,很容易想到的一种解决方法就是统一交互的方式,SOA无疑是实现这种方式的首选指导思想。
    SOA全称为面向服务架构,其强调系统之间以标准的服务方式进行交互,各系统可采用不同的语言、不同的框架进行实现,需要交互时则全部通过服务的方式来进行。

    eBay也实现了一个SOA平台来支撑其业务的多元化发展 ,eBay认为SOA能给其带来的最大好处为提升业务的重用以及灵敏性,SOA平台除了要实现统一的交互外,还会碰到其他多方面的挑战,来看看eBay眼中SOA平台会带来哪些挑战:
     服务多级调用带来的延时;
    当出现一个功能需要调用服务A,服务A又需要调用服务B,服务B又需要调用服务C时,这个时候会带来大幅度的延时,解决延时需要做到的是高性能的服务交互,以及完善的服务调用过程控制,例如当调用在服务B执行时已超时,那么则没必要继续调用服务C,而应该直接抛出超时异常给调用端。
     调试/跟踪困难
    例如当调用服务A,服务A报错时,调用者通常会去找服务A的开发人员来解决,服务A的开发人员去查问题,有可能会发现是由于服务B报错,服务B的开发人员去查,有可能发现是网络有问题,在这样的情况下,就会导致出现问题时调试/跟踪非常困难。
     更高的安全/监测的要求
    在未拆分系统时,对系统的安全和监测都只需要在一个系统上做即可,当拆分后,就必须在每个系统上都有相应的安全控制以及监测。
     现有应用移植
    这一点是SOA平台在推广时的大挑战,需要移植的应用上的功能越多,就需要花费越多的时间来完成移植。
     QoS的支持
    每个服务提供者能够支撑的访问量必然是有限的,因此设定服务的QoS非常重要,并且应尽可能采取一系列的措施来保障QoS,例如流量控制、机器资源分配等。
     高可用和高度可伸缩
    这是互联网应用必须做到的两点,SOA平台承担了所有服务的交互,因此其可用性以及伸缩性就更会影响巨大。
     多版本和依赖管理
    随着服务不断发展,以及使用面不断扩大,服务多版本的存在会成为一个大的需求,同时由于服务众多,这些服务的依赖关系也需要管理起来,以便在升级时能进行相应的安排。

    eBay根据这些挑战自行实现了一个SOA平台,这个SOA平台主要包含了以下几点:
     高性能、可扩展的轻量级框架;
     监测、安全控制、流量控制的支持;
     服务注册和服务仓库;
     开发工具;

    在推行SOA平台时,eBay采取的为按领域驱动的方式划分服务,同时不断的培训开发人员,以便让开发人员明确服务化后和之前单系统的方式的区别,例如可能会产生网络通信错误、超时等。

    综合上面的内容来看,对于一个大型应用中的SOA平台,至少应包含以下几点功能:
     统一的服务交互方式,并可实现和现有应用的无缝集成;
     提供调试/跟踪的支持;
     依赖管理;
     高性能以及高可用。

    在实现SOA时,可参考的标准或概念有SCA、ESB,同时业界也有一些实现了SCA、ESB的框架,下面就来具体的看看SCA、ESB、SCA框架以及ESB框架。

    《Web容量规划的艺术》书评

    twitter上@fire9给我推荐了这本书,花了一些时间把这本书看了两遍,总结性的点评语就是:“书的质量非常的高,一方面这本书中的内容来源于flickr.com实际的经验,另一方面是作者采用了很多生活中的例子来讲解一些复杂的技术,让人很快就明白了。”下面就具体来看看这本书传达的容量规划该怎么做。

    twitter上@fire9给我推荐了这本书,花了一些时间把这本书看了两遍,总结性的点评语就是:“书的质量非常的高,一方面这本书中的内容来源于flickr.com实际的经验,另一方面是作者采用了很多生活中的例子来讲解一些复杂的技术,让人很快就明白了。”下面就具体来看看这本书传达的容量规划该怎么做。

    容量规划主要分为四个步骤来进行:

    1、设定容量的目标:例如网站需要在3秒内响应,达到99.99%的可用性。

    2、收集对应的指标并找出面临的限制
    这个步骤需要做的为:
    测量和记录服务器的主要功能,例如数据库的主要功能为插入数据、删除数据、更新数据和获取数据,在这里书中举了个例子是没有油量表的车;
    测量和记录基础硬件资源,例如cpu、文件IO、网络IO和内存的消耗;
    判断服务器的主要功能如何与硬件资源关联,判断的方法为寻找主要功能增长时硬件资源的主要增长点,例如在书中的例子为flickr.com中数据库的主从复制的延时率与磁盘的IO关联,web服务器能支撑的请求量和cpu资源关联;
    基于主要功能、硬件资源和容量目标,找出在目前的资源情况下所能承担的上限(例如复制延时率最多能接受的为延迟180ms,此时磁盘IO大概为40%,就此就可制对磁盘IO报警的阈值,并可观察此时系统能支撑的并发量),这里书中提到的一个方法为在生产环境中通过负载均衡来引导真实的流量来测试上限。
    测量对于容量规划而言至关重要,因此书中写到了测量不是可选的,是必须的。

    3、绘制趋势并根据指标和限制进行预测
    基于历史数据进行曲线拟合,工具可采用excel或ftiyk,从而预测什么时候目前的资源将达到上限阀值;另外需要考虑的一点是如何制定红线,例如CPU消耗最多能接受多少,这里要根据历史的偶发高峰状况做个安全值的考虑,假设历史数据显示偶然比平时高个8%是常见的话,就至少要留个10%左右的安全空间。

    4、容量部署和管理
    基于上面的预测就可制定采购计划或架构调整计划了,到底什么时候需要采购机器是个复杂的问题,这个和零库存面临的问题基本是一样的;另外就是当机器到了后如何快速完成部署,因此需要自动化的部署工具。

    按照上面的步骤不断的迭代可逐步的做好容量规划,但实践起来必然会有很多的难点,容量规划和性能调优的最大差别在于容量规划是基于现有状况来评估什么时候需要什么资源来支撑网站的运行,而性能调优更多的纯粹是考虑如何提升性能。

    书中最后还提及到了如何应对突发增长的情况,对于突发增长的状况,由于不可能提前准备,书中给的几个建议是:禁用重量级的功能、临时采用静态页面、缓存而不是过期以及采用更为人性化的方式处理故障,最后一点我觉得最有意思,这也是我们现在处理故障时遗漏的,书中举了一个例子来比喻,就是如果厨房被淹了,此时有个水管工在处理的话,你会感觉到至少有人在处理,而一个好的水管工还会告诉你原因和解决的方法,在这样的情况下也许你能对这个故障更加容忍了。

    最后,强烈向希望能够预测什么时候需要增加多少资源,或做什么架构调整的同学推荐这本书,相信会给你带来很多的感触,毕竟这是来源于flickr.com的实际经验。

    Unicorn给Twitter带来的效果

    3月30日Twitter在其engineering blog上写了一篇Unicorn Power的blog:http://engineering.twitter.com/2010/03/unicorn-power.html,写的挺经典的,按我的理解来讲下这篇blog吧,如有错误,请帮忙纠正,:)。

    3月30日Twitter在其engineering blog上写了一篇Unicorn Power的blog:http://engineering.twitter.com/2010/03/unicorn-power.html,写的挺经典的,按我的理解来讲下这篇blog吧,如有错误,请帮忙纠正,:)。
    @sandofsky和@netik在blog中首先举了个经典的超市收银的问题,通常超市会有多个收银台,在高峰期通常每个收银台前都会排队,而这个时候如果某个顾客买的东西特别多,或者某个顾客买东西时遇到麻烦的话,就会导致队伍后面的人要排很久的队,而其实此时其他的队有可能处理的更快很多,这种情况会造成的问题是有些人先排的队,但却等了更长很多的时间,于是有家叫Fry的超市,采取了另外一种方法来进行收银,就是让所有的顾客在同一个地方排队,然后三十个收银员进行收银,每完成一个时就按下信号灯,表示可以去该收银台进行付款了,这样之后的好处是减少了由于个别顾客造成一堆人等待很长时间的现象。
    从超市的例子脱离出来,来看看twitter的状况,之前twitter运行在apache和mongrel上,通过mod_proxy_balancer来做负载均衡,每台业务处理器能处理一定的请求,处理不过来的就排队,于是就有可能出现有些用户不幸的在处理比较慢的机器中排队,在排了长时间后可能会出现的是超时,而另外一个问题就是队列排不下了,就只能丢弃请求了,但其实也许这个时候如果请求都合理的分配的话,可能能够让超时或丢弃请求的现象降低,于是twitter的工程师开始寻求改变。
    在去年11月,twitter开始尝试Unicorn,unicorn增加了一个类似Fry超市的拉的方式,twitter首先在mobile.twitter.com上进行了尝试,到了今年1月,已经在twitter.com上也采用了unicorn,在采用Unicorn了后,twitter惊喜的发现,它们明显的降低了30%的请求延时,并且还很明显的降低了CPU使用率。
    其他的是一些监测方面的介绍,就不在此分析了,感兴趣的同学可以看看原作。

    这篇blog给了我挺大的感触的,在标准的通过硬件负载设备访问real server时,尤其典型的通过硬件负载设备访问后端tomcat的情况中,会出现的现象就和twitter的现象基本一样了,假设一个tomcat只能处理100个请求,为了避免用户太高的失败率,必然会给一个较大的等待队列,这个时候就会出现如果请求处理慢,就导致了排在队中的用户要等待很久,从而造成超时或请求丢弃,如果能采取类似Fry的方法,那么就能够做到按照用户发起请求的顺序来进行处理了,而不会因为个别用户请求处理慢就造成排队的用户超时和请求被拒绝的现象,这确实是一个合理发挥server请求处理能力的负载均衡不同角度的策略,但这里其实有个要求,就是负载设备或软件负载方案要能支持排队功能,这对传统的方案还是有一定挑战的(例如最少连接数),但无论如何,其能带来的效果还是明显的。

    ps: 不过如果一家超市只有一个队的话,那得多长呀…有些时候可能会给顾客造成心理压力,搞不好就不买了,呵呵,当然,应用到软件领域倒是没这个问题,反正用户也不知道现在排的队有多长,:)