Java 垃圾回收策略调优

Posted on

Java 垃圾回收策略调优-实践篇

登录 注册

九点 阿尔法城 移动应用

豆瓣小组

KK

来自: KK 2008-10-22 13:26:30

JVM参数调优是一个很头痛的问题,可能和应用有关系,下面是本人一些调优的实践经验,希望对读者能有帮助,环境LinuxAS4,resin2.1.17,JDK6.0,2CPU,4G内存,dell2950服务器,网站是shedewang.com,新手可能觉得这文章没有用。 一:串行垃圾回收,也就是默认配置,完成10万request用时153秒,JVM参数配置如下 $JAVA_ARGS .= " -Dresin.home=$SERVER_ROOT -server -Xms2048M -Xmx2048M -Xmn512M -XX:PermSize=256M -XX:MaxPermSize=256M -XX:MaxTenuringThreshold=7 -XX:GCTimeRatio=19 -Xnoclassgc -Xloggc:log/gc.log -XX:+PrintGCDetails -XX:+PrintGCTimeStamps "; 这种配置一般在resin启动24小时内似乎没有大问题,网站可以正常访问,但查看日志发现,在接近24小时时,Full GC执行越来越频繁,大约每隔3分钟就有一次Full GC,每次Full GC系统会停顿6秒左右,作为一个网站来说,用户等待6秒恐怕太长了,所以这种方式有待改善。MaxTenuringThreshold=7表示一个对象如果在救助空间移动7次还没有被回收就放入年老代,GCTimeRatio=19表示java可以用5%的时间来做垃圾回收,1/(1+19)=1 /20=5%。 二:并行回收,完成10万request用时117秒,配置如下: $JAVA_ARGS .= " -Dresin.home=$SERVER_ROOT -server -Xmx2048M -Xms2048M -Xmn512M -XX:PermSize=256M -XX:MaxPermSize=256M -Xnoclassgc -Xloggc:log/gc.log -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+UseParallelGC -XX:ParallelGCThreads=20 -XX:+UseParallelOldGC -XX:MaxGCPauseMillis=500 -XX:+UseAdaptiveSizePolicy -XX:MaxTenuringThreshold=7 -XX:GCTimeRatio=19 "; 并行回收我尝试过多种组合配置,似乎都没什么用,resin启动3小时左右就会停顿,时间超过10 秒。也有可能是参数设置不够好的原因,MaxGCPauseMillis表示GC最大停顿时间,在resin刚启动还没有执行Full GC时系统是正常的,但一旦执行Full GC,MaxGCPauseMillis根本没有用,停顿时间可能超过20秒,之后会发生什么我也不再关心了,赶紧重启resin,尝试其他回收策略。 三:并发回收,完成10万request用时60秒,比并行回收差不多快一倍,是默认回收策略性能的2.5倍,配置如下: $JAVA_ARGS .= " -Dresin.home=$SERVER_ROOT -server -Xms2048M -Xmx2048M -Xmn512M -XX:PermSize=256M -XX:MaxPermSize=256M -XX:+UseConcMarkSweepGC -XX:MaxTenuringThreshold=7 -XX:GCTimeRatio=19 -Xnoclassgc -Xloggc:log/gc.log -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=0 "; 这个配置虽然不会出现10秒连不上的情况,但系统重启3个小时左右,每隔几分钟就会有5秒连不上的情况,查看gc.log,发现在执行ParNewGC时有个promotion failed错误,从而转向执行Full GC,造成系统停顿,而且会很频繁,每隔几分钟就有一次,所以还得改善。UseCMSCompactAtFullCollection是表是执行Full GC后对内存进行整理压缩,免得产生内存碎片,CMSFullGCsBeforeCompaction=N表示执行N次Full GC后执行内存压缩。 四:增量回收,完成10万request用时171秒,太慢了,配置如下 $JAVA_ARGS .= " -Dresin.home=$SERVER_ROOT -server -Xms2048M -Xmx2048M -Xmn512M -XX:PermSize=256M -XX:MaxPermSize=256M -XX:MaxTenuringThreshold=7 -XX:GCTimeRatio=19 -Xnoclassgc -Xloggc:log/gc.log -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xincgc "; 似乎回收得也不太干净,而且也对性能有较大影响,不值得试。 五:并发回收的I-CMS模式,和增量回收差不多,完成10万request用时170秒。 $JAVA_ARGS .= " -Dresin.home=$SERVER_ROOT -server -Xms2048M -Xmx2048M -Xmn512M -XX:PermSize=256M -XX:MaxPermSize=256M -XX:MaxTenuringThreshold=7 -XX:GCTimeRatio=19 -Xnoclassgc -Xloggc:log/gc.log -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+UseConcMarkSweepGC -XX:+CMSIncrementalMode -XX:+CMSIncrementalPacing -XX:CMSIncrementalDutyCycleMin=0 -XX:CMSIncrementalDutyCycle=10 -XX:-TraceClassUnloading "; 采用了sun推荐的参数,回收效果不好,照样有停顿,数小时之内就会频繁出现停顿,什么sun推荐的参数,照样不好使。 六:递增式低暂停收集器,还叫什么火车式回收,不知道属于哪个系,完成10万request用时153秒 $JAVA_ARGS .= " -Dresin.home=$SERVER_ROOT -server -Xms2048M -Xmx2048M -Xmn512M -XX:PermSize=256M -XX:MaxPermSize=256M -XX:MaxTenuringThreshold=7 -XX:GCTimeRatio=19 -Xnoclassgc -Xloggc:log/gc.log -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+UseTrainGC "; 该配置效果也不好,影响性能,所以没试。 七:相比之下,还是并发回收比较好,性能比较高,只要能解决ParNewGC(并行回收年轻代)时的promotion failed错误就一切好办了,查了很多文章,发现引起promotion failed错误的原因是CMS来不及回收(CMS默认在年老代占到90%左右才会执行),年老代又没有足够的空间供GC把一些活的对象从年轻代移到年老代,所以执行Full GC。CMSInitiatingOccupancyFraction=70表示年老代占到约70%时就开始执行CMS,这样就不会出现Full GC了。SoftRefLRUPolicyMSPerMB这个参数也是我认为比较有用的,官方解释是softly reachable objects will remain alive for some amount of time after the last time they were referenced. The default value is one second of lifetime per free megabyte in the heap,我觉得没必要等1秒,所以设置成0。配置如下 $JAVA_ARGS .= " -Dresin.home=$SERVER_ROOT -server -Xms2048M -Xmx2048M -Xmn512M -XX:PermSize=256M -XX:MaxPermSize=256M -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=7 -XX:GCTimeRatio=19 -Xnoclassgc -XX:+DisableExplicitGC -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+CMSPermGenSweepingEnabled -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=0 -XX:+CMSClassUnloadingEnabled -XX:-CMSParallelRemarkEnabled -XX:CMSInitiatingOccupancyFraction=70 -XX:SoftRefLRUPolicyMSPerMB=0 -XX:+PrintClassHistogram -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCApplicationConcurrentTime -XX:+PrintGCApplicationStoppedTime -Xloggc:log/gc.log "; 上面这个配置内存上升的很慢,24小时之内几乎没有停顿现象,最长的只停滞了0.8s,ParNew GC每30秒左右才执行一次,每次回收约0.2秒,看来问题应该暂时解决了。 参数不明白的可以上网查,本人认为比较重要的几个参数是:-Xms -Xmx -Xmn MaxTenuringThreshold GCTimeRatio UseConcMarkSweepGC CMSInitiatingOccupancyFraction SoftRefLRUPolicyMSPerMB 7人 喜欢 喜欢

回应 推荐 喜欢 只看楼主

  • 肖

(@Beijing) 2008-10-22 22:53:34

首先使用64 bit os,是一个提高性能的不二法规. 第二,任何调优应该有场景,在未知情况应该使用loggc导出日志分析,再加参数.对于参数的好与坏,我希望楼主能贴出更多的自已的思路

回应 删除

  • KK

KK 2008-10-27 15:32:12

还有,如果遇到内存泄漏,要用jstack和jmap、jstat等多看看,应该可以找到问题,还要多看看gc日志各个区域内存使用情况和gc频率,这样才可以作出最佳配置。Good Luck

回应 删除

  • KK

KK 2008-11-03 17:37:21

内存问题和应用有很大关系,我的应用因为用到了缓存,所以年老代比较大,多看看gc日志,出了问题多用Jstack,jmap等工具查看,这样才可以最大的优化JVM参数。经过进两个星期的调试,我的配置是/usr/local/jdk/bin/java -Dresin.home=/usr/local/resin -server -Xms1800M -Xmx1800M -Xmn300M -Xss512K -XX:PermSize=300M -XX:MaxPermSize=300M -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=5 -XX:GCTimeRatio=19 -Xnoclassgc -XX:+DisableExplicitGC -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=0 -XX:-CMSParallelRemarkEnabled -XX:CMSInitiatingOccupancyFraction=70 -XX:SoftRefLRUPolicyMSPerMB=0 -XX:+PrintClassHistogram -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintHeapAtGC -Xloggc:log/gc.log 72小时之内没有一点停顿,我是32位LinuxAS4操作系统。

回应 删除

  • Foxswily

Foxswily 2008-11-03 17:45:09

mark先,前一段做一个对性能要求高的项目,典型的高DB负载,把着眼点都放在了DB优化上(最终效果也不错,大概提高了40倍左右的性能),一直没有考虑jvm优化,疏忽了。 but,jvm的优化性价比貌似不是很高,化很大力气得到回报如何有待实践检验。

回应 删除

  • KK

KK 2008-11-03 17:55:02

不是纯粹性能问题,到一定时候JVM参数必须优化,不然访问量大的时候会造成频繁的Full GC或者CMS,Full GC有时能让整个系统停顿10秒,10秒内用户的任何点击都在等待状态,要是隔2分钟就来一次Full GC,恐怕用户和老板都受不了了。 并发回收如果配置的不好照样会产生Full GC。:)

回应 删除

  • Foxswily

Foxswily 2008-11-03 17:58:43

有道理,支持这种有内容、有见地的讨论贴。

回应 删除

  • 洛臻星轮

洛臻星轮 2012-05-22 23:22:11

感谢下,用了里面的很多参数,目前好使了,让它再多跑跑 项目中Full GC一次停顿了个快十秒,一停顿事件就积压,隔段时间又停顿,事件积压得越来越严重,程序就崩了...

回应 删除

  • alzq 2012-07-20 13:00:19

好配置,实验中……效果不错~

回应 删除

你的回应

回应请先 登录 , 或 注册

推荐到广播

Java&Android移动应用编程

9641 人聚集在这个小组 加入小组

最新话题 ( 更多 )

手机扫描二维码,让小组随时陪伴你

App Store下载 点击直接下载

© 2005-2013 douban.com, all rights reserved 关于豆瓣 · 在豆瓣工作 · 联系我们 · 免责声明 · 帮助中心 · 开发者 · 手机小组 · 豆瓣广告

↑回顶部 ×

动态跟踪Java代码的执行状况工具

Posted on

动态跟踪Java代码的执行状况工具--BTrace

BlueDavy之技术Blog

理论不懂就实践,实践不会就学理论!

动态跟踪Java代码的执行状况工具--BTrace

非常强烈的推荐下BTrace这个工具,用了后不得不说太强大了,BTrace简单来说,就是能在不改动当前程序的情况下,运行时的去监控Java程序的执行状况,例如可以做到内存状况的监控、方法调用的监控等等,官方网站上有非常多详细的例子,我不说太多,只在下面举一个简单的例子来说明它的作用,BTrace的User Guide请见:http://kenai.com/projects/btrace/pages/UserGuide。 对于运行中的Java程序,尤其是出了问题的程序,会需要跟踪其执行状况,例如传入的参数是什么、执行了多少时间,返回的对象是什么,抛出了什么异常,传统的做法只能是把程序改一遍,加上一堆log,一个例子来展示下用BTrace的情况下,怎么来跟踪一个方法的执行时间: @BTrace public class MethodResponseTime {

@TLS private static long startTime;

@OnMethod(clazz="类名",method="方法名")
public static void onCall(){
    println("enter this method");
    startTime=timeMillis();
}

@OnMethod(clazz="类名",method="方法名",location=@Location(Kind.RETURN))
public static void onReturn(){
    println("method end!");
    println(strcat("Time taken ms",str(timeMillis()-startTime)));
}

} 用btrace执行上面的代码,就可以动态的监控任意的目前运行的Java程序中某类的某方法的执行时间,执行上面代码的方式如下(jdk 6+): btrace [pid] MethodResponseTime.class 还有例如获取调用参数、调用者的对象实例以及返回值等请参看User Guide。 btrace为了保持JVM运行的安全性,因此做了很多的限制,例如不能抛出异常、修改传入的参数的值、修改返回值等,基本是一个只读的动态分析代码运行状况的工具,但仍然是非常的有用,其实现机制是attach api + asm + instrumentation。

posted on 2009-10-10 12:41 BlueDavy 阅读(11121) 评论(8) 编辑 收藏 所属分类: Java

[

评论

]()

不错,这个有点强悍的,使用一下先 回复 更多评论

收藏了,以后可能用得着。 回复 更多评论

要钱吗? 回复 更多评论

试了一下trace Apache Felix 2.0.0, 已加载了的类不好使,抛出NullPointerException。 回复 更多评论

貌似没有什么界面,还得用命令行啊~~~ 回复 更多评论

visualvm有btrace的插件 回复 更多评论

林昊朋友,你好, 首先祝贺你的blog被ZDNet评为了“最受欢迎中国技术博客”之一。 不过你的照片……不知你弄错了还是ZDNet弄错了,是我的头像呵。 详情参考: http://blog.linjian.org/articles/my-photo-misused-again/ 有机会看看你写的书。我前不久也与博文视点合作过,是《我是一只IT小小鸟》的合作者之一。 回复 更多评论

@sswv ...我并不知道ZDNET评选这件事情,我联系下他们吧,不好意思了,可是现在相当火的书,非常恭喜。 回复 更多评论 新用户注册 刷新评论列表

推荐购买云服务器(15%返利+最高千元奖金) 博客园 博问 IT新闻 Java程序员招聘 标题 请输入标题 姓名 请输入你的姓名 主页 请输入验证码 验证码 /* 内容(请不要发表任何与政治相关的内容) 请输入评论内容 Remember Me? 登录 [使用Ctrl+Enter键可以直接提交] 网站导航:

博客园 IT新闻 知识库 C++博客 程序员招聘 管理 相关文章:

feedsky 抓虾 google reader 鲜果

导航

统计

  • 随笔 - 294
  • 文章 - 2
  • 评论 - 2068
  • 引用 - 1

随笔分类

随笔档案

文章档案

Blogger's

搜索

*

最新评论

阅读排行榜

评论排行榜

JVM调优的

Posted on

JVM调优的"标准参数"的各种陷阱

您还未登录 ! 登录 注册

ITeye3.0

群组首页编程语言高级语言虚拟机论坛

发表回复

« 上一页 1 2 3 下一页 »

[讨论] JVM调优的"标准参数"的各种陷阱

RednaxelaFX的博客 RednaxelaFX 2011-10-23

开个帖大家来讨论下自己遇到过的情况吧?我在顶楼举几个例子。 开这帖的目的是想让大家了解到,所谓“标准参数”是件很微妙的事情。确实有许多前辈经过多年开发积累下了许多有用的调优经验,但向他们问“标准参数”并照单全收是件危险的事情。 前辈们提供的“标准参数”或许适用于他们的应用场景,他们或许也知道这些参数里隐含的陷阱;但听众却不一定知道各种参数背后的缘由。 原则上说,在生产环境使用非标准参数(这里指的是在各JDK/JRE实现特有的、相互之间不通用的参数)应该尽量避免。这些参数与具体实现密切相关,不是光了解很抽象的“JVM原理”就足以理解的;即便在同一系列的JDK/JRE实现中,非标准参数也不保证在各版本间有一样的作用;而且许多人只看名字就猜想参数的左右,做“调优”却适得其反。 非标准参数的默认值在不同版本间或许会悄然发生变化。这些变化的背后多半有合理的理由。设了一大堆非标准参数、不明就里的同学在升级JDK/JRE的时候也容易掉坑里。 下面用Oracle/Sun JDK 6来举几个例子。这帖顶楼里的讨论如果没明确指出JDK版本的都是指Oracle/Sun JDK 6(OpenJDK 6也可以算在内)。

经验不一定适用于Sun JDK 1.4.2、Sun JDK 5、Oracle JDK 7。

0、各参数的默认值 在讨论HotSpot VM的各参数的陷阱前,大家应该先了解HotSpot VM到底有哪些参数可以设置,这些参数的默认值都是什么。 有几种办法可以帮助大家获取参数的信息。首先为了大致了解都有些什么参数可以设置,可以参考HotSpot VM里的各个globals.hpp文件:(以下链接取自HotSpot 20.0,与JDK 6 update 25对应) globals.hpp globals_extension.hpp c1_globals.hpp c1_globals_linux.hpp c1_globals_solaris.hpp c1_globals_sparc.hpp c1_globals_windows.hpp c1_globals_x86.hpp c2_globals.hpp c2_globals_linux.hpp c2_globals_solaris.hpp c2_globals_sparc.hpp c2_globals_windows.hpp c2_globals_x86.hpp g1_globals.hpp globals_linux.hpp globals_linux_sparc.hpp globals_linux_x86.hpp globals_linux_zero.hpp globals_solaris.hpp globals_solaris_sparc.hpp globals_solaris_x86.hpp globals_sparc.hpp globals_windows.hpp globals_windows_x86.hpp globals_x86.hpp globals_zero.hpp shark_globals.hpp shark_globals_zero.hpp arguments.cpp 然后是 -XX:+PrintCommandLineFlags 。这个参数的作用是显示出VM初始化完毕后所有跟最初的默认值不同的参数及它们的值。 这个参数至少在Sun JDK 5上已经开始支持,Oracle/Sun JDK 6以及Oracle JDK 7上也可以使用。Sun JDK 1.4.2还不支持这个参数。 例子: Command prompt代码 复制代码 收藏代码

  1. $ java -XX:+PrintCommandLineFlags
  2. VM option '+PrintCommandLineFlags'
  3. -XX:InitialHeapSize=57344000 -XX:MaxHeapSize=917504000 -XX:ParallelGCThreads=4 -XX:+PrintCommandLineFlags -XX:+UseCompressedOops -XX:+UseParallelGC
    $ java -XX:+PrintCommandLineFlags VM option '+PrintCommandLineFlags' -XX:InitialHeapSize=57344000 -XX:MaxHeapSize=917504000 -XX:ParallelGCThreads=4 -XX:+PrintCommandLineFlags -XX:+UseCompressedOops -XX:+UseParallelGC 《Java Performance》一书主要是用这个参数来介绍HotSpot VM各参数的效果的。 接着是 -XX:+PrintFlagsFinal 。前一个参数只显示跟默认值不同的,而这个参数则可以显示所有可设置的参数及它们的值。不过这个参数本身只从JDK 6 update 21开始才可以用,之前的Oracle/Sun JDK则用不了。 可以设置的参数默认是不包括diagnostic或experimental系的。要在-XX:+PrintFlagsFinal的输出里看到这两种参数的信息,分别需要显式指定-XX:+UnlockDiagnosticVMOptions / -XX:+UnlockExperimentalVMOptions 。 再下来,-XX:+PrintFlagsInitial 。这个参数显示在处理参数之前所有可设置的参数及它们的值,然后直接退出程序。“参数处理”包括许多步骤,例如说检查参数之间是否有冲突,通过ergonomics调整某些参数的值,之类的。 结合-XX:+PrintFlagsInitial与-XX:+PrintFlagsFinal,对比两者的差异,就可以知道ergonomics对哪些参数做了怎样的调整。 这两个参数的例子: Command prompt代码 复制代码 收藏代码

  4. $ java -version

  5. java version "1.6.0_29"
  6. Java(TM) SE Runtime Environment (build 1.6.0_29-b11)
  7. Java HotSpot(TM) 64-Bit Server VM (build 20.4-b02, mixed mode)
  8. $ java -XX:+PrintFlagsInitial | grep UseCompressedOops
  9. bool UseCompressedOops = false {lp64_product}
  10. $ java -XX:+PrintFlagsFinal | grep UseCompressedOops
  11. bool UseCompressedOops := true {lp64_product}
    $ java -version java version "1.6.0_29" Java(TM) SE Runtime Environment (build 1.6.0_29-b11) Java HotSpot(TM) 64-Bit Server VM (build 20.4-b02, mixed mode) $ java -XX:+PrintFlagsInitial | grep UseCompressedOops bool UseCompressedOops = false {lp64_product} $ java -XX:+PrintFlagsFinal | grep UseCompressedOops bool UseCompressedOops := true {lp64_product} Oracle/Sun JDK 6从update 23开始会由ergonomics在合适的条件下默认启用压缩指针功能。这个例子演示的是UseCompressedOops的初始默认值是false,由PrintFlagsInitial的输出可以看到;然后经过ergonomics自动调整后,最终采用的默认值是true,由PrintFlagsFinal的输出可以看到。 输出里“=”表示使用的是初始默认值,而“:=”表示使用的不是初始默认值,可能是命令行传进来的参数、配置文件里的参数或者是ergonomics自动选择了别的值。 除了在VM启动时传些特殊的参数让它打印出自己的各参数外,jinfo -flag 可以用来查看某个参数的值,也可以用来设定manageable系参数的值。请参考这帖的例子:通过jinfo工具在full GC前后做heap dump

    之前发过某些环境中HotSpot VM的各参数的默认值,可以参考一下。

    1、-XX:+DisableExplicitGC 与 NIO的direct memory 很多人都见过JVM调优建议里使用这个参数,对吧?但是为什么要用它,什么时候应该用而什么时候用了会掉坑里呢? 首先要了解的是这个参数的作用。在Oracle/Sun JDK这个具体实现上,System.gc()的默认效果是引发一次stop-the-world的full GC,对整个GC堆做收集。有几个参数可以改变默认行为,之前发过一帖简单描述过,这里就不重复了。关键点是,用了-XX:+DisableExplicitGC参数后,System.gc()的调用就会变成一个空调用,完全不会触发任何GC(但是“函数调用”本身的开销还是存在的哦~)。 为啥要用这个参数呢?最主要的原因是为了防止某些手贱的同学在代码里到处写System.gc()的调用而干扰了程序的正常运行吧。有些应用程序本来可能正常跑一天也不会出一次full GC,但就是因为有人在代码里调用了System.gc()而不得不间歇性被暂停。也有些时候这些调用是在某些库或框架里写的,改不了它们的代码但又不想被这些调用干扰也会用这参数。 OK。看起来这参数应该总是开着嘛。有啥坑呢? 其中一种情况是下述三个条件同时满足时会发生的: 1、应用本身在GC堆内的对象行为良好,正常情况下很久都不发生full GC; 2、应用大量使用了NIO的direct memory,经常、反复的申请DirectByteBuffer 3、使用了-XX:+DisableExplicitGC 能观察到的现象是: Log代码 复制代码 收藏代码

  12. java.lang.OutOfMemoryError: Direct buffer memory

  13. at java.nio.Bits.reserveMemory(Bits.java:633)
  14. at java.nio.DirectByteBuffer.(DirectByteBuffer.java:98)
  15. at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:288)
  16. ...
    java.lang.OutOfMemoryError: Direct buffer memory at java.nio.Bits.reserveMemory(Bits.java:633) at java.nio.DirectByteBuffer.(DirectByteBuffer.java:98) at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:288) ... 做个简单的例子来演示这现象: Java代码 复制代码 收藏代码

  17. import java.nio./*;

  18. public class DisableExplicitGCDemo {
  19. public static void main(String[] args) {
  20. for (int i = 0; i < 100000; i++) {
  21. ByteBuffer.allocateDirect(128);
  22. }
  23. System.out.println("Done");
  24. }
  25. }
    import java.nio./*; public class DisableExplicitGCDemo { public static void main(String[] args) { for (int i = 0; i < 100000; i++) { ByteBuffer.allocateDirect(128); } System.out.println("Done"); } } 然后编译、运行之: Command prompt代码 复制代码 收藏代码

  26. $ java -version

  27. java version "1.6.0_25"
  28. Java(TM) SE Runtime Environment (build 1.6.0_25-b06)
  29. Java HotSpot(TM) 64-Bit Server VM (build 20.0-b11, mixed mode)
  30. $ javac DisableExplicitGCDemo.java
  31. $ java -XX:MaxDirectMemorySize=10m -XX:+PrintGC -XX:+DisableExplicitGC DisableExplicitGCDemo
  32. Exception in thread "main" java.lang.OutOfMemoryError: Direct buffer memory
  33. at java.nio.Bits.reserveMemory(Bits.java:633)
  34. at java.nio.DirectByteBuffer.(DirectByteBuffer.java:98)
  35. at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:288)
  36. at DisableExplicitGCDemo.main(DisableExplicitGCDemo.java:6)
  37. $ java -XX:MaxDirectMemorySize=10m -XX:+PrintGC DisableExplicitGCDemo
  38. [GC 10996K->10480K(120704K), 0.0433980 secs]
  39. [Full GC 10480K->10415K(120704K), 0.0359420 secs]
  40. Done
    $ java -version java version "1.6.0_25" Java(TM) SE Runtime Environment (build 1.6.0_25-b06) Java HotSpot(TM) 64-Bit Server VM (build 20.0-b11, mixed mode) $ javac DisableExplicitGCDemo.java $ java -XX:MaxDirectMemorySize=10m -XX:+PrintGC -XX:+DisableExplicitGC DisableExplicitGCDemo Exception in thread "main" java.lang.OutOfMemoryError: Direct buffer memory at java.nio.Bits.reserveMemory(Bits.java:633) at java.nio.DirectByteBuffer.(DirectByteBuffer.java:98) at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:288) at DisableExplicitGCDemo.main(DisableExplicitGCDemo.java:6) $ java -XX:MaxDirectMemorySize=10m -XX:+PrintGC DisableExplicitGCDemo [GC 10996K->10480K(120704K), 0.0433980 secs] [Full GC 10480K->10415K(120704K), 0.0359420 secs] Done 可以看到,同样的程序,不带-XX:+DisableExplicitGC时能正常完成运行,而带上这个参数后却出现了OOM。 例子里用-XX:MaxDirectMemorySize=10m限制了DirectByteBuffer能分配的空间的限额,以便问题更容易展现出来。不用这个参数就得多跑一会儿了。 在这个例子里,main()里的循环不断申请DirectByteBuffer但并没有引用、使用它们,所以这些DirectByteBuffer应该刚创建出来就已经满足被GC的条件,等下次GC运行的时候就应该可以被回收。 实际上却没这么简单。DirectByteBuffer是种典型的“冰山”对象,也就是说它的Java对象虽然很小很无辜,但它背后却会关联着一定量的native memory资源,而这些资源并不在GC的控制之下,需要自己注意控制好。对JVM如何使用native memory不熟悉的同学可以参考去年JavaOne上IBM的一个演讲,“Where Does All the Native Memory Go”。 Oracle/Sun JDK的实现里,DirectByteBuffer有几处值得注意的地方。 1、DirectByteBuffer没有finalizer,它的native memory的清理工作是通过sun.misc.Cleaner自动完成的。 2、sun.misc.Cleaner是一种基于PhantomReference的清理工具,比普通的finalizer轻量些。对PhantomReference不熟悉的同学请参考Bob Lee最近几年在JavaOne上做的演讲,"The Ghost in the Virtual Machine: A Reference to References"今年的JavaOne上他也讲了同一个主题,内容比前几年的稍微更新了些。PPT可以从链接里的页面下载到。 Java代码 复制代码 收藏代码

  41. ///

  42. /* General-purpose phantom-reference-based cleaners.
  43. /*
  44. /*

    Cleaners are a lightweight and more robust alternative to finalization.

  45. /* They are lightweight because they are not created by the VM and thus do not
  46. /* require a JNI upcall to be created, and because their cleanup code is
  47. /* invoked directly by the reference-handler thread rather than by the
  48. /* finalizer thread. They are more robust because they use phantom references,
  49. /* the weakest type of reference object, thereby avoiding the nasty ordering
  50. /* problems inherent to finalization.
  51. /*
  52. /*

    A cleaner tracks a referent object and encapsulates a thunk of arbitrary

  53. /* cleanup code. Some time after the GC detects that a cleaner's referent has
  54. /* become phantom-reachable, the reference-handler thread will run the cleaner.
  55. /* Cleaners may also be invoked directly; they are thread safe and ensure that
  56. /* they run their thunks at most once.
  57. /*
  58. /*

    Cleaners are not a replacement for finalization. They should be used

  59. /* only when the cleanup code is extremely simple and straightforward.
  60. /* Nontrivial cleaners are inadvisable since they risk blocking the
  61. /* reference-handler thread and delaying further cleanup and finalization.
  62. /*
  63. /*
  64. /* @author Mark Reinhold
  65. /* @version %I%, %E%
  66. //
    //
    / / General-purpose phantom-reference-based cleaners. / /

    Cleaners are a lightweight and more robust alternative to finalization. / They are lightweight because they are not created by the VM and thus do not / require a JNI upcall to be created, and because their cleanup code is / invoked directly by the reference-handler thread rather than by the / finalizer thread. They are more robust because they use phantom references, / the weakest type of reference object, thereby avoiding the nasty ordering / problems inherent to finalization. / /

    A cleaner tracks a referent object and encapsulates a thunk of arbitrary / cleanup code. Some time after the GC detects that a cleaner's referent has / become phantom-reachable, the reference-handler thread will run the cleaner. / Cleaners may also be invoked directly; they are thread safe and ensure that / they run their thunks at most once. / /

    Cleaners are not a replacement for finalization. They should be used / only when the cleanup code is extremely simple and straightforward. / Nontrivial cleaners are inadvisable since they risk blocking the / reference-handler thread and delaying further cleanup and finalization. / / / @author Mark Reinhold / @version %I%, %E% // 重点是这两句:"A cleaner tracks a referent object and encapsulates a thunk of arbitrary cleanup code. Some time after the GC detects that a cleaner's referent has become phantom-reachable, the reference-handler thread will run the cleaner." Oracle/Sun JDK 6中的HotSpot VM只会在old gen GC(full GC/major GC或者concurrent GC都算)的时候才会对old gen中的对象做reference processing,而在young GC/minor GC时只会对young gen里的对象做reference processing。 (死在young gen中的DirectByteBuffer对象会在young GC时被处理的例子,请参考这里:https://gist.github.com/1614952) 也就是说,做full GC的话会对old gen做reference processing,进而能触发Cleaner对已死的DirectByteBuffer对象做清理工作。而如果很长一段时间里没做过GC或者只做了young GC的话则不会在old gen触发Cleaner的工作,那么就可能让本来已经死了的、但已经晋升到old gen的DirectByteBuffer关联的native memory得不到及时释放。 3、为DirectByteBuffer分配空间过程中会显式调用System.gc(),以期通过full GC来强迫已经无用的DirectByteBuffer对象释放掉它们关联的native memory: Java代码 复制代码 收藏代码

  67. // These methods should be called whenever direct memory is allocated or

  68. // freed. They allow the user to control the amount of direct memory
  69. // which a process may access. All sizes are specified in bytes.
  70. static void reserveMemory(long size) {
  71. synchronized (Bits.class) {
  72. if (!memoryLimitSet && VM.isBooted()) {
  73. maxMemory = VM.maxDirectMemory();
  74. memoryLimitSet = true;
  75. }
  76. if (size <= maxMemory - reservedMemory) {
  77. reservedMemory += size;
  78. return;
  79. }
  80. }
  81. System.gc();
  82. try {
  83. Thread.sleep(100);
  84. } catch (InterruptedException x) {
  85. // Restore interrupt status
  86. Thread.currentThread().interrupt();
  87. }
  88. synchronized (Bits.class) {
  89. if (reservedMemory + size > maxMemory)
  90. throw new OutOfMemoryError("Direct buffer memory");
  91. reservedMemory += size;
  92. }
  93. }
    // These methods should be called whenever direct memory is allocated or // freed. They allow the user to control the amount of direct memory // which a process may access. All sizes are specified in bytes. static void reserveMemory(long size) { synchronized (Bits.class) { if (!memoryLimitSet && VM.isBooted()) { maxMemory = VM.maxDirectMemory(); memoryLimitSet = true; } if (size <= maxMemory - reservedMemory) { reservedMemory += size; return; } } System.gc(); try { Thread.sleep(100); } catch (InterruptedException x) { // Restore interrupt status Thread.currentThread().interrupt(); } synchronized (Bits.class) { if (reservedMemory + size > maxMemory) throw new OutOfMemoryError("Direct buffer memory"); reservedMemory += size; } } 这几个实现特征使得Oracle/Sun JDK 6依赖于System.gc()触发GC来保证DirectByteMemory的清理工作能及时完成。如果打开了-XX:+DisableExplicitGC,清理工作就可能得不到及时完成,于是就有机会见到direct memory的OOM,也就是上面的例子演示的情况。我们这边在实际生产环境中确实遇到过这样的问题。

    教训是:如果你在使用Oracle/Sun JDK 6,应用里有任何地方用了direct memory,那么使用-XX:+DisableExplicitGC要小心。如果用了该参数而且遇到direct memory的OOM,可以尝试去掉该参数看是否能避开这种OOM。如果担心System.gc()调用造成full GC频繁,可以尝试下面提到 -XX:+ExplicitGCInvokesConcurrent 参数

    2、-XX:+DisableExplicitGC 与 Remote Method Invocation (RMI) 与 -Dsun.rmi.dgc.{server|client}.gcInterval= 看了上一个例子有没有觉得-XX:+DisableExplicitGC参数用起来很危险?那干脆完全不要用这个参数吧。又有什么坑呢? 前段时间有个应用的开发来抱怨,说某次升级JDK之前那应用的GC状况都很好,很长时间都不会发生full GC,但升级后发现每一小时左右就会发生一次。经过对比发现,升级的同时也吧启动参数改了,把原本有的-XX:+DisableExplicitGC给去掉了。 观察到的日志有明显特征。一位同事表示: 引用

线上机器出现一个场景;每隔1小时出现一次Full GC,用btrace看了一下调用地: who call system.gc : sun.misc.GC$Daemon.run(GC.java:92) 预发机没什么流量,也会每一小时一次Full GC 频率正好是一小时一次 Gc log代码 复制代码 收藏代码

  1. 2011-09-23T10:49:38.071+0800: 327692.227: [Full GC (System) 327692.227: [CMS: 75793K->75759K(2097152K), 0.6923690 secs] 430298K->75759K(3984640K), [CMS Perm : 104136K->104124K(173932K)], 0.6925570 secs]
    2011-09-23T10:49:38.071+0800: 327692.227: [Full GC (System) 327692.227: [CMS: 75793K->75759K(2097152K), 0.6923690 secs] 430298K->75759K(3984640K), [CMS Perm : 104136K->104124K(173932K)], 0.6925570 secs] 实际上这里在做的是分布式GC。Sun JDK的分布式GC是用纯Java实现的,为RMI服务。 RMI DGC相关参数的介绍文档:http://docs.oracle.com/javase/6/docs/technotes/guides/rmi/sunrmiproperties.html (后面回头再补…先睡觉去了) 资料: jGuru: Distributed Garbage Collection http://mail.openjdk.java.net/pipermail/hotspot-dev/2011-December/004909.html Tony Printezis 写道

RMI has a distributed GC that relies on reference processing to allow each node to recognize that some objects are unreachable so it can notify a remote node (or nodes) that some remote references to them do not exist any more. The remote node might then be able to reclaim objects that are only remotely reachable. (Or this is how I understood it at least.) RMI used to call System.gc() once a minute (!!!) but after some encouragement from yours truly they changed the default to once an hour (this is configurable using a property). Note that a STW Full GC is not really required as long as references are processed. So, in CMS (and G1), a concurrent cycle is fine which is why we recommend to use -XX:+ExplicitGCInvokesConcurrent in this case. I had been warned by the RMI folks against totally disabling those System.gc()'s (e.g., using -XX:+DisableExplicitGC) given that if Full GCs / concurrent cycles do not otherwise happen at a reasonable frequency then remote nodes might experience memory leaks since they will consider that some otherwise unreachable remote references are still live. I have no idea how severe such memory leaks would be. I guess they'd be very application-dependent. An additional thought that just occurred to me: instead of calling System.gc() every hour what RMI should really be doing is calling System.gc() every hour provided no old gen GC has taken place during the last hour. This would be relatively easy to implement by accessing the old GC counter through the GC MXBeans. Tony 再加俩链接:http://mail.openjdk.java.net/pipermail/hotspot-dev/2012-January/004929.html http://mail.openjdk.java.net/pipermail/hotspot-dev/2012-January/004946.html

《Java Performance》的303和411页正好也提到了-XX:+DisableExplicitGC与RMI之间的干扰的事情,有兴趣可以读一下,虽然只有一小段。

3、-XX:+ExplicitGCInvokesConcurrent 或 -XX:+ExplicitGCInvokesConcurrentAndUnloadsClasses C++代码 复制代码 收藏代码

  1. product(bool, ExplicitGCInvokesConcurrent, false, \
  2. "A System.gc() request invokes a concurrent collection;" \
  3. " (effective only when UseConcMarkSweepGC)") \
  4. \
  5. product(bool, ExplicitGCInvokesConcurrentAndUnloadsClasses, false, \
  6. "A System.gc() request invokes a concurrent collection and " \
  7. "also unloads classes during such a concurrent gc cycle " \
  8. "(effective only when UseConcMarkSweepGC)") \
    product(bool, ExplicitGCInvokesConcurrent, false, \ "A System.gc() request invokes a concurrent collection;" \ " (effective only when UseConcMarkSweepGC)") \ \ product(bool, ExplicitGCInvokesConcurrentAndUnloadsClasses, false, \ "A System.gc() request invokes a concurrent collection and " \ "also unloads classes during such a concurrent gc cycle " \ "(effective only when UseConcMarkSweepGC)") \ 跟上面的第一个例子的-XX:+DisableExplicitGC一样,这两个参数也是用来改变System.gc()的默认行为用的;不同的是这两个参数只能配合CMS使用(-XX:+UseConcMarkSweepGC),而且System.gc()还是会触发GC的,只不过不是触发一个完全stop-the-world的full GC,而是一次并发GC周期。 CMS GC周期中也会做reference processing。所以如果用这两个参数的其中一个,而不是用-XX:+DisableExplicitGC的话,就避开了由full GC带来的长GC pause,同时NIO direct memory的OOM也不会那么容易发生。 做了个跟第一个例子类似的例子,在这里:https://gist.github.com/1344251 《Java Performance》的303页有讲到这俩参数。 相关bug:6919638 CMS: ExplicitGCInvokesConcurrent misinteracts with gc locker

    << JDK6u23修复了这个问题

    4、-XX:+GCLockerInvokesConcurrent C++代码 复制代码 收藏代码

  9. product(bool, GCLockerInvokesConcurrent, false, \

  10. "The exit of a JNI CS necessitating a scavenge also" \
  11. " kicks off a bkgrd concurrent collection") \
    product(bool, GCLockerInvokesConcurrent, false, \ "The exit of a JNI CS necessitating a scavenge also" \ " kicks off a bkgrd concurrent collection") \

    (内容回头补…)

    5、MaxDirectMemorySize 与 NIO direct memory 的默认上限 -XX:MaxDirectMemorySize 是用来配置NIO direct memory上限用的VM参数。 C++代码 复制代码 收藏代码

  12. product(intx, MaxDirectMemorySize, -1, \

  13. "Maximum total size of NIO direct-buffer allocations") \
    product(intx, MaxDirectMemorySize, -1, \ "Maximum total size of NIO direct-buffer allocations") \ 但如果不配置它的话,direct memory默认最多能申请多少内存呢?这个参数默认值是-1,显然不是一个“有效值”。所以真正的默认值肯定是从别的地方来的。 在Sun JDK 6和OpenJDK 6里,有这样一段代码,sun.misc.VM: Java代码 复制代码 收藏代码

  14. // A user-settable upper limit on the maximum amount of allocatable direct

  15. // buffer memory. This value may be changed during VM initialization if
  16. // "java" is launched with "-XX:MaxDirectMemorySize=".
  17. //
  18. // The initial value of this field is arbitrary; during JRE initialization
  19. // it will be reset to the value specified on the command line, if any,
  20. // otherwise to Runtime.getRuntime().maxMemory().
  21. //
  22. private static long directMemory = 64 / 1024 / 1024;
  23. // If this method is invoked during VM initialization, it initializes the
  24. // maximum amount of allocatable direct buffer memory (in bytes) from the
  25. // system property sun.nio.MaxDirectMemorySize. The system property will
  26. // be removed when it is accessed.
  27. //
  28. // If this method is invoked after the VM is booted, it returns the
  29. // maximum amount of allocatable direct buffer memory.
  30. //
  31. public static long maxDirectMemory() {
  32. if (booted)
  33. return directMemory;
  34. Properties p = System.getProperties();
  35. String s = (String)p.remove("sun.nio.MaxDirectMemorySize");
  36. System.setProperties(p);
  37. if (s != null) {
  38. if (s.equals("-1")) {
  39. // -XX:MaxDirectMemorySize not given, take default
  40. directMemory = Runtime.getRuntime().maxMemory();
  41. } else {
  42. long l = Long.parseLong(s);
  43. if (l > -1)
  44. directMemory = l;
  45. }
  46. }
  47. return directMemory;
  48. }
    // A user-settable upper limit on the maximum amount of allocatable direct // buffer memory. This value may be changed during VM initialization if // "java" is launched with "-XX:MaxDirectMemorySize=". // // The initial value of this field is arbitrary; during JRE initialization // it will be reset to the value specified on the command line, if any, // otherwise to Runtime.getRuntime().maxMemory(). // private static long directMemory = 64 / 1024 / 1024; // If this method is invoked during VM initialization, it initializes the // maximum amount of allocatable direct buffer memory (in bytes) from the // system property sun.nio.MaxDirectMemorySize. The system property will // be removed when it is accessed. // // If this method is invoked after the VM is booted, it returns the // maximum amount of allocatable direct buffer memory. // public static long maxDirectMemory() { if (booted) return directMemory; Properties p = System.getProperties(); String s = (String)p.remove("sun.nio.MaxDirectMemorySize"); System.setProperties(p); if (s != null) { if (s.equals("-1")) { // -XX:MaxDirectMemorySize not given, take default directMemory = Runtime.getRuntime().maxMemory(); } else { long l = Long.parseLong(s); if (l > -1) directMemory = l; } } return directMemory; } (代码里原本的注释有个写错的地方,上面有修正) 当MaxDirectMemorySize参数没被显式设置时它的值就是-1,在Java类库初始化时maxDirectMemory()被java.lang.System的静态构造器调用,走的路径就是这条: Java代码 复制代码 收藏代码

  49. if (s.equals("-1")) {

  50. // -XX:MaxDirectMemorySize not given, take default
  51. directMemory = Runtime.getRuntime().maxMemory();
  52. }
    if (s.equals("-1")) { // -XX:MaxDirectMemorySize not given, take default directMemory = Runtime.getRuntime().maxMemory(); } 而Runtime.maxMemory()在HotSpot VM里的实现是: C++代码 复制代码 收藏代码

  53. JVM_ENTRY_NO_ENV(jlong, JVM_MaxMemory(void))

  54. JVMWrapper("JVM_MaxMemory");
  55. size_t n = Universe::heap()->max_capacity();
  56. return convert_size_t_to_jlong(n);
  57. JVM_END
    JVM_ENTRY_NO_ENV(jlong, JVM_MaxMemory(void)) JVMWrapper("JVM_MaxMemory"); size_t n = Universe::heap()->max_capacity(); return convert_size_t_to_jlong(n); JVM_END 这个max_capacity()实际返回的是 -Xmx减去一个survivor space的预留大小(G1除外)。 结论:MaxDirectMemorySize没显式配置的时候,NIO direct memory可申请的空间的上限就是-Xmx减去一个survivor space的预留大小。 大家感兴趣的话可以试试在不同的-Xmx的条件下不设置MaxDirectMemorySize,并且调用一下sun.misc.VM.maxDirectMemory()看得到的值的相关性。

    该行为在JDK7里没变,虽然具体实现的代码有些变化。请参考http://hg.openjdk.java.net/jdk7/jdk7/jdk/rev/b444f86c4abe

    6、-verbose:gc 与 -XX:+PrintGCDetails 经常能看到在推荐的标准参数里这两个参数一起出现。实际上它们有啥关系? 在Oracle/Sun JDK 6里,"java"这个启动程序遇到"-verbosegc"会将其转换为"-verbose:gc",将启动参数传给HotSpot VM后,HotSpot VM遇到"-verbose:gc"则会当作"-XX:+PrintGC"来处理。 也就是说 -verbosegc、-verbose:gc、-XX:+PrintGC 三者的作用是完全一样的。 而当HotSpot VM遇到 -XX:+PrintGCDetails 参数时,会顺带把 -XX:+PrintGC 给设置上。 也就是说 -XX:+PrintGCDetails 包含 -XX:+PrintGC,进而也就包含 -verbose:gc。

    既然 -verbose:gc 都被包含了,何必在命令行参数里显式设置它呢?

    7、-XX:+UseFastEmptyMethods 与 -XX:+UseFastAccessorMethods 虽然不常见,但偶尔也会见到推荐的标准参数上有这俩的身影。 empty method顾名思义就是空方法,也就是方法体只包含一条return指令、返回值类型为void的Java方法。 accessor method在这里则有很具体的定义: C++代码 复制代码 收藏代码

  58. bool methodOopDesc::is_accessor() const {

  59. if (code_size() != 5) return false;
  60. if (size_of_parameters() != 1) return false;
  61. if (java_code_at(0) != Bytecodes::_aload_0 ) return false;
  62. if (java_code_at(1) != Bytecodes::_getfield) return false;
  63. if (java_code_at(4) != Bytecodes::_areturn &&
  64. java_code_at(4) != Bytecodes::_ireturn ) return false;
  65. return true;
  66. }
    bool methodOopDesc::is_accessor() const { if (code_size() != 5) return false; if (size_of_parameters() != 1) return false; if (java_code_at(0) != Bytecodes::_aload_0 ) return false; if (java_code_at(1) != Bytecodes::_getfield) return false; if (java_code_at(4) != Bytecodes::_areturn && java_code_at(4) != Bytecodes::_ireturn ) return false; return true; } 如果从Java源码的角度来理解,accessor method就是形如这样的: Java代码 复制代码 收藏代码

  67. public class Foo {

  68. private int value;
  69. public int getValue() {
  70. return this.value;
  71. }
  72. }
    public class Foo { private int value; public int getValue() { return this.value; } } 关键点是: 1、必须是成员方法;静态方法不行 2、返回值类型必须是引用类型或者int,其它都不算 3、方法体的代码必须满足aload_0; getfield /#index; areturn或ireturn这样的模式。 留意:方法名是什么都没关系,是不是get、is、has开头都不重要。 那么这俩有啥问题? 取自JDK 6 update 27: C++代码 复制代码 收藏代码

  73. product(bool, UseFastEmptyMethods, true, \

  74. "Use fast method entry code for empty methods") \
  75. \
  76. product(bool, UseFastAccessorMethods, true, \
  77. "Use fast method entry code for accessor methods") \
    product(bool, UseFastEmptyMethods, true, \ "Use fast method entry code for empty methods") \ \ product(bool, UseFastAccessorMethods, true, \ "Use fast method entry code for accessor methods") \ 看到这俩参数的默认值都是true了么?也就是说,在Oracle/Sun JDK 6上设置这参数其实也是没意义的,跟默认一样,一直到最新的JDK 6 update 29都是如此。 不过在Oracle/Sun JDK 7里,情况有变化。 Bug ID: 6385687 UseFastEmptyMethods/UseFastAccessorMethods considered harmful上述bug对应的代码变更后,这俩参数的默认值改为了false。 本来想多写点这块的…算,还是长话短说。 Oracle JDK 7里的HotSpot VM已经开始有比较好的多层编译(tiered compilation)支持,可以预见在不久的将来该模式将成为HotSpot VM默认的执行模式。当前该模式尚未默认开启;可以通过 -XX:+TieredCompilation 来开启。 有趣的是,在使用多层编译模式时,如果UseFastAccessorMethods/UseFastEmptyMethods是开着的,有些多态方法调用点的性能反而会显著下降。所以,为了适应多层编译模式,JDK 7里这两个参数的默认值就被改为false了。

    在邮件列表上有过相关讨论:review for 6385687: UseFastEmptyMethods/UseFastAccessorMethods considered harmful

    8、-XX:+UseCMSCompactAtFullCollection 这个参数在Oracle/Sun JDK 6里一直都默认是true,完全没必要显式设置,设了也不会有啥不同的效果。 C++代码 复制代码 收藏代码

  78. product(bool, UseCMSCompactAtFullCollection, true, \

  79. "Use mark sweep compact at full collections") \
    product(bool, UseCMSCompactAtFullCollection, true, \ "Use mark sweep compact at full collections") \ 我不认为显式设置一个跟默认值相同的参数有什么维护上的好处。要维护的参数多了反而更容易成为维护的噩梦吧。后面的人会不知道到底当初为什么要设置这个参数。 相关的有个 CMSFullGCsBeforeCompaction 参数,请参考另一帖里的讨论:http://hllvm.group.iteye.com/group/topic/28854/#209294 同样,在Oracle/Sun JDK 6和OpenJDK 6里,CMSParallelRemarkEnabled 也一直默认是true,没必要显式设置-XX:+CMSParallelRemarkEnabled。 有很多bool类型的参数默认都是true,显式设置它们之前最好先用这帖开头介绍的办法看看默认值是否已经是想要的值了。 C++代码 复制代码 收藏代码

  80. product(bool, CMSScavengeBeforeRemark, false, \

  81. "Attempt scavenge before the CMS remark step") \
    product(bool, CMSScavengeBeforeRemark, false, \ "Attempt scavenge before the CMS remark step") \

    这个默认倒是false。如果一个应用统计到的young GC时间都比较短而CMS remark的时间比较长,那么可以试试打开这个参数,在做remark之前先做一次young GC。是否能有效缩短remark的时间视应用情况而异,所以开这个参数的话请一定做好测试。

    9、-XX:CMSMaxAbortablePrecleanTime=5000 同上…默认就是5000 C++代码 复制代码 收藏代码

  82. product(intx, CMSMaxAbortablePrecleanTime, 5000, \

  83. "(Temporary, subject to experimentation)" \
  84. "Maximum time in abortable preclean in ms") \
    product(intx, CMSMaxAbortablePrecleanTime, 5000, \ "(Temporary, subject to experimentation)" \ "Maximum time in abortable preclean in ms") \

    还是不要设跟默认值一样的参数了吧。

    10、-Xss 与 -XX:ThreadStackSize 参考我之前发过的两帖: What the difference between -Xss and -XX:ThreadStackSize is? Inconsistency between -Xss and -XX:ThreadStackSize in the java launcher

    (详情回头补~)

    11、-Xmn 与 -XX:NewSize、-XX:MaxNewSize

    如果同时设置了-XX:NewSize与-XX:MaxNewSize遇到“Could not reserve enough space for object heap”错误的话,请看看是不是这帖所说的问题。早期JDK 6似乎都受这问题影响,一直到JDK 6 update 14才修复。

    12、-Xmn 与 -XX:NewRatio

    13、-XX:NewRatio 与 -XX:NewSize、-XX:OldSize

    14、jmap -heap看到的参数值与实际起作用的参数的关系? 发了几个例子在这里:https://gist.github.com/1363195 其中有个看起来很恐怖的值: Java代码 复制代码 收藏代码

  85. MaxNewSize = 17592186044415 MB
    MaxNewSize = 17592186044415 MB 这是啥来的? C++代码 复制代码 收藏代码

  86. product(uintx, MaxNewSize, max_uintx, \

  87. "Maximum new generation size (in bytes), max_uintx means set " \
  88. "ergonomically")
    product(uintx, MaxNewSize, max_uintx, \ "Maximum new generation size (in bytes), max_uintx means set " \ "ergonomically") 在HotSpot VM里,intx是跟平台字长一样宽的带符号整型,uintx是其无符号版。 max_uintx是(uintx) -1,也就是说在32位平台上是无符号的0xFFFFFFFF,64位平台上则是0xFFFFFFFFFFFFFFFF。 jmap -heap显示的部分参数是以MB为单位来显示的,而MaxNewSize的单位是byte。我跑例子的平台是64位的,于是算一下 0xFFFFFFFFFFFFFFFF / 1024 / 1024 = 17592186044415 MB 。 参数的说明告诉我们,当MaxNewSize的值等于max_uintx时,意思就是交由ergonomics来自动选择young gen的最大大小。并不是说young gen的最大大小真的有0xFFFFFFFFFFFFFFFF这么大。 要注意的是,HotSpot VM有大量可调节的参数,并不是所有参数在某次运行的时候都有效。 例如说设置了-Xmn的话,NewRatio就没作用了。 又例如说, C++代码 复制代码 收藏代码

  89. product(uintx, OldSize, ScaleForWordSize(4/*M), \

  90. "Initial tenured generation size (in bytes)") \
    product(uintx, OldSize, ScaleForWordSize(4/*M), \ "Initial tenured generation size (in bytes)") \ -XX:OldSize参数的默认值在32位平台上是4M,在64位平台上是5M多。但如果这个参数没有被显式设置过,那它实际上是没作用的;old gen的大小会通过Java heap的整体大小与young gen的大小配置计算出来,但OldSize参数却没有被更新(因为根本没用它)。于是这个参数的值与实际运行的状况就可能会不相符。 一种例外的情况是,如果-Xmx非常小,比NewSize+OldSize的默认值还小,那这个OldSize的默认值就会起作用,把MaxHeapSize给撑大。 C++代码 复制代码 收藏代码

  91. void TwoGenerationCollectorPolicy::initialize_flags() {

  92. GenCollectorPolicy::initialize_flags();
  93. OldSize = align_size_down(OldSize, min_alignment());
  94. if (NewSize + OldSize > MaxHeapSize) {
  95. MaxHeapSize = NewSize + OldSize;
  96. }
  97. MaxHeapSize = align_size_up(MaxHeapSize, max_alignment());
  98. //...
  99. }
    void TwoGenerationCollectorPolicy::initialize_flags() { GenCollectorPolicy::initialize_flags(); OldSize = align_size_down(OldSize, min_alignment()); if (NewSize + OldSize > MaxHeapSize) { MaxHeapSize = NewSize + OldSize; } MaxHeapSize = align_size_up(MaxHeapSize, max_alignment()); //... }

    可以看这边的一个例子:https://gist.github.com/1375782

    15、-XX:+AlwaysTenure、-XX:+NeverTenure、-XX:MaxTenuringThreshold=0 或 "-XX:MaxTenuringThreshold=markOopDesc::max_age + 1" ParNew的时候,设定-XX:+AlwaysTenure隐含-XX:MaxTenuringThreshold=0;不过-XX:+NeverTenure却没啥特别的作用。

    -XX:+AlwaysTenure 与 -XX:+NeverTenure 是互斥的,最后一个出现的那个会同时决定这两个参数的值。

    16、-XX:MaxTenuringThreshold 的默认值? C++代码 复制代码 收藏代码

  100. product(intx, MaxTenuringThreshold, 15, \

  101. "Maximum value for tenuring threshold") \
    product(intx, MaxTenuringThreshold, 15, \ "Maximum value for tenuring threshold") \ Oracle/Sun JDK 6中,选择CMS之外的GC时,MaxTenuringThreshold(以下简称MTT)的默认值是15;而选择了CMS的时候,MTT的默认值是4而不是15。设定是在 Arguments::set_cms_and_parnew_gc_flags() 里做的。 在Sun JDK 6之前(1.4.2、5),选择CMS的时候MTT的默认值则是0,也就是等于设定了-XX:+AlwaysTenure——所有eden里的活对象在经历第一次minor GC的时候就会直接晋升到old gen,而survivor space直接就没用了。 Command prompt代码 复制代码 收藏代码

  102. $ java -version

  103. java version "1.6.0_25"
  104. Java(TM) SE Runtime Environment (build 1.6.0_25-b06)
  105. Java HotSpot(TM) 64-Bit Server VM (build 20.0-b11, mixed mode)
  106. $ java -XX:+PrintFlagsFinal | egrep 'MaxTenuringThreshold|UseParallelGC|UseConcMarkSweepGC'
  107. intx MaxTenuringThreshold = 15 {product}
  108. bool UseConcMarkSweepGC = false {product}
  109. bool UseParallelGC := true {product}
  110. $ java -XX:+PrintFlagsFinal -XX:+UseConcMarkSweepGC | egrep 'MaxTenuringThreshold|UseParallelGC|UseConcMarkSweepGC'
  111. intx MaxTenuringThreshold := 4 {product}
  112. bool UseConcMarkSweepGC := true {product}
  113. bool UseParallelGC = false {product}

    $ java -version java version "1.6.0_25" Java(TM) SE Runtime Environment (build 1.6.0_25-b06) Java HotSpot(TM) 64-Bit Server VM (build 20.0-b11, mixed mode) $ java -XX:+PrintFlagsFinal | egrep 'MaxTenuringThreshold|UseParallelGC|UseConcMarkSweepGC' intx MaxTenuringThreshold = 15 {product} bool UseConcMarkSweepGC = false {product} bool UseParallelGC := true {product} $ java -XX:+PrintFlagsFinal -XX:+UseConcMarkSweepGC | egrep 'MaxTenuringThreshold|UseParallelGC|UseConcMarkSweepGC' intx MaxTenuringThreshold := 4 {product} bool UseConcMarkSweepGC := true {product} bool UseParallelGC = false {product}

    17、-XX:+CMSClassUnloadingEnabled

    CMS remark暂停时间会增加,所以如果类加载并不频繁、String的intern也没有大量使用的话,这个参数还是不开的好。

    18、-XX:+AggressiveHeap

    19、-XX:+UseCompressedOops 有益?有害? 先把微博上回复别人问题的解答放这边。 本来如果功能没bug的话,Oracle/Sun JDK 6的64位HotSpot上,GC堆在26G以下(-Xmx + -XX:MaxPermSize)的时候用多数都是有益的。 开启压缩指针后,从代码路径(code path)和CPI(cycles per instruction)两个角度看,情况是不一样的: ·开启压缩指针会使代码路径变长,因为所有在GC堆里的、指向GC堆内对象的指针都会被压缩,这些指针的访问就需要更多的代码才可以实现。不要以为只是读写字段才受影响,其实实例方法调用、子类型检查等操作也受影响——“klass”也是一个指针,也被压缩了。 ·但从CPI的角度看,由于压缩指针使需要拷贝的数据量变小了,cache miss的几率随之降低,结果CPI可能会比压缩前降低。综合来看,开了压缩指针通常能大幅降低GC堆内存的消耗,同时维持或略提高Java程序的速度。 但,JDK6u23之前那个参数的bug实在太多,最好别用;而6u23之后它就由ergonomics自动开启了,不用自己设。如果在6u23或更高版本碰到压缩指针造成的问题的话,显式设置 -XX:-UseCompressedOops 。 我能做的建议是如果在64位Oracle/Sun JDK 6/7上,那个参数不要显式设置。 关于HotSpot VM的ergonomics自动开启压缩指针功能,请参考之前的一帖

    有些库比较“聪明”,会自行读取VM参数来调整自己的一些参数,例如Berkeley DB Java Edition。但这些库实现得不好的时候反而会带来一些麻烦:BDB JE要求显式指定-XX:+UseCompressedOops才能有效的调整它的缓存大小。所以在用BDB JE并且Java堆+PermGen大小小于32GB的时候,请显式指定-XX:+UseCompressedOops吧。参考Warning on Compressed Oops

    20、-XX:LargePageSizeInBytes=128m ? 或者是 -XX:LargePageSizeInBytes=256m ? 其实这个参数的值是多少不是问题,问题是这个参数到底有没有起作用。 或许有人读过很老的调优建议资料,例如这个: (2005) Java Tuning White Paper - 4.2.3 Tuning Example 3: Try 256 MB pages 或者是别的一些内容很老的资料。它们提到了-XX:LargePageSizeInBytes=参数。这些老资料也没说错,在Sun JDK 5里 -XX:LargePageSizeInBytes= 参数只在Solaris上有效,使用的时候没有别的参数保护。 但是,实际上这个参数在Oracle/Sun JDK 6里不配合-XX:+UseLargePages的话是不会起任何作用的。 JDK 6里的JVM的Linux版上初始化large page的地方: C++代码 复制代码 收藏代码

  114. bool os::large_page_init() {

  115. if (!UseLargePages) return false;
  116. if (LargePageSizeInBytes) {
  117. _large_page_size = LargePageSizeInBytes;
  118. } else {
  119. // ...
  120. }
  121. // ...
  122. // Large page support is available on 2.6 or newer kernel, some vendors
  123. // (e.g. Redhat) have backported it to their 2.4 based distributions.
  124. // We optimistically assume the support is available. If later it turns out
  125. // not true, VM will automatically switch to use regular page size.
  126. return true;
  127. }
    bool os::large_page_init() { if (!UseLargePages) return false; if (LargePageSizeInBytes) { _large_page_size = LargePageSizeInBytes; } else { // ... } // ... // Large page support is available on 2.6 or newer kernel, some vendors // (e.g. Redhat) have backported it to their 2.4 based distributions. // We optimistically assume the support is available. If later it turns out // not true, VM will automatically switch to use regular page size. return true; } 看到了么,没有将UseLargePages设置为true的话,LargePageSizeInBytes根本没机会被用上。 对应的,Solaris版: C++代码 复制代码 收藏代码

  128. bool os::large_page_init() {

  129. if (!UseLargePages) {
  130. UseISM = false;
  131. UseMPSS = false;
  132. return false;
  133. }
  134. // ...
  135. }
    bool os::large_page_init() { if (!UseLargePages) { UseISM = false; UseMPSS = false; return false; } // ... } 以及Windows版: C++代码 复制代码 收藏代码

  136. bool os::large_page_init() {

  137. if (!UseLargePages) return false;
  138. // ...
  139. }
    bool os::large_page_init() { if (!UseLargePages) return false; // ... }

    在Oracle/Sun JDK 6以及Oracle JDK 7上要使用 -XX:LargePageSizeInBytes= 的话,请务必也设置上 -XX:+UseLargePages 。使用这两个参数之前最好先确认操作系统是否真的只是large pages;操作系统不支持的话,设置这两个参数也没作用,只会退回到使用regular pages而已。

    21、-XX:+AlwaysPreTouch 会把commit的空间跑循环赋值为0以达到“pretouch”的目的。开这个参数会增加VM初始化时的开销,但后面涉及虚拟内存的开销可能降低。

    在另一个讨论帖里有讲该参数:http://hllvm.group.iteye.com/group/topic/28839/#209144

    22、-XX:+UseTLAB 与 Runtime.freeMemory()

    23、-XX:+ParallelRefProcEnabled 这个功能可以加速reference processing,但在JDK6u25和6u26上不要使用,有bug:

    Bug ID 7028845: CMS: 6984287 broke parallel reference processing in CMS

    24、-XX:+UseConcMarkSweepGC 与 -XX:+UseAdaptiveSizePolicy 这两个选项在现有的Oracle/Sun JDK 6和Oracle JDK 7上都不要搭配在一起使用——CMS用的adaptive size policy还没实现完,用的话可能会crash。 目前HotSpot VM上只有ParallelScavenge系的GC才可以配合-XX:+UseAdaptiveSizePolicy使用;也就是只有-XX:+UseParallelGC或者-XX:+UseParallelOldGC。Jon Masamitsu在邮件列表上提到过

    题外话:开着UseAdaptiveSizePolicy的ParallelScavenge会动态调整各空间的大小,有可能会造成两个survivor space的大小被调整得不一样大。Jon Masamitsu在这封邮件里解释了原因。

    25、-XX:+UseAdaptiveGCBoundary

    JDK 6里不要用这个选项,有bug。

    26、-XX:HeapDumpPath 与 -XX:+HeapDumpBeforeFullGC、-XX:+HeapDumpAfterFullGC、-XX:+HeapDumpOnOutOfMemoryError -XX:+HeapDumpBeforeFullGC、-XX:+HeapDumpAfterFullGC、-XX:+HeapDumpOnOutOfMemoryError 这几个参数可以在不同条件下做出HPROF格式的heap dump。但很多人都会疑惑:做出来的heap dump存到哪里去了? 如果不想费神去摸索到底各种环境被配置成什么样、“working directory”到底在哪里的话,就在VM启动参数里加上 -XX:HeapDumpPath=一个绝对路径 吧。这样,自动做出的heap dump就会被存到指定的目录里去。

    当然相对路径也支持,不过用了相对路径就又得弄清楚当前的“working directory”在哪里了。

    26、UseDepthFirstScavengeOrder 以前有过这样一个参数可以设置young gen遍历对象图的顺序,深度还是广度优先不过高于JDK 6 update 22就没用了,ParallelScavenge变为只用深度优先而不用广度优先。 具体的changeset在这里:http://hg.openjdk.java.net/hsx/hotspot-main/hotspot/rev/9d7a8ab3736b HotSpot VM里的arguments.cpp文件里有obsolete_jvm_flags数组,那边声明的参数都要留意是已经没用的。 (下文待续~) Spinner learnworld的博客 learnworld 2011-10-23

好贴,坐等下文。 Spinner jolestar的博客 jolestar 2011-10-23

lz不厚道,说了个半句话就闪了。太吊胃口了。 Spinner boy00fly的博客 boy00fly 2011-10-24

jolestar 写道

lz不厚道,说了个半句话就闪了。太吊胃口了。 哈哈,确实吊胃口。。下文呢? Spinner icanfly的博客 icanfly 2011-10-24

好文。 Spinner liuyes的博客 liuyes 2011-10-24

楼主人呢?这发文的也有坑 Spinner khotyn的博客 khotyn 2011-10-24

坐等R大把坑填上~~ Spinner 程序新手的博客 程序新手 2011-10-24

内容受益、风格简洁,期待下文 另外大胆猜测从日志中获取的信息是部分FULL GC由System.gc()引发的 Full GC (System) Spinner 程序新手的博客 程序新手 2011-10-25

又受益了 ..想了解大大收集这些知识点的方法~ Spinner hittyt的博客 hittyt 2011-11-08

本人的java环境如下: Command line代码 复制代码 收藏代码

  1. java -version
  2. java version "1.6.0_18"
  3. Java(TM) SE Runtime Environment (build 1.6.0_18-b07)
  4. Java HotSpot(TM) 64-Bit Server VM (build 16.0-b13, mixed mode)
    java -version java version "1.6.0_18" Java(TM) SE Runtime Environment (build 1.6.0_18-b07) Java HotSpot(TM) 64-Bit Server VM (build 16.0-b13, mixed mode) 但使用PrintFlagsFinal时得到的输出却是下面的结果: Command line代码 复制代码 收藏代码

  5. java -XX:+PrintFlagsFinal

  6. Unrecognized VM option '+PrintFlagsFinal'
  7. Could not create the Java virtual machine.
    java -XX:+PrintFlagsFinal Unrecognized VM option '+PrintFlagsFinal' Could not create the Java virtual machine. 求LZ解答问题在哪里呢? Spinner

发表回复

« 上一页 1 2 3 下一页 »

>>返回群组首页

高级语言虚拟机群组

相关讨论

© 2003-2012 ITeye.com. [ 京ICP证110151号 京公网安备110105010620 ] 百联优力(北京)投资有限公司 版权所有

豆列:从表到里学习JVM实现

Posted on

豆列:从表到里学习JVM实现

刚写了个学习JVM用的豆列跟大家分享。 豆列地址:http://book.douban.com/doulist/2545443/ 下面把豆列的介绍贴过来。具体书单请到上面的豆列地址那边去看。 在社会化分工、软件行业细分专业化的趋势下,会真的参与到底层系统实现的人肯定是越来越少(比例上说)。真的会参与到JVM实现的人肯定是少数。 但如果您对JVM是如何实现的有兴趣、充满好奇,却苦于没有足够系统的知识去深入,那么这个豆列就是为您打好基础而准备的。 如果只想用用Java用用JVM,对深入到实现细节无甚兴趣的话,这个豆列就请不必参考了,免得浪费钱浪费时间精力,呵呵 :-) 本豆列的脉络是:

  1. JVM与Java语言规范 要了解JVM是如何实现的,首先必须要知道JVM到底是什么、不是什么,表面上应该提供怎样的功能。为此,JVM规范必读,而且应该时常放在手边参考。 而JVM的主要服务对象是Java编程语言。虽然JVM也可以支持众多其它语言,但JVM里的“J”仍然最重要,Java的语言特性影响了JVM的原始设计,所以Java语言规范也应该阅读。特别是,JVM关于线程和同步相关的规定都是交由Java语言规范的相关章节定义的。
  2. 虚拟机概论 这里选取《Virtual Machines: Versatile Platforms for Systems and Processes》,帮助您了解“虚拟机”一词到底指代什么,有什么不同类型,大概有哪些实现方法,等等。读完这本书有助获得一个清晰的大局观。
  3. 为Java程序员从用户的角度介绍JVM的使用经验的几本书 虽然这几本并没有深入到JVM实现的非常细节的角落,但对已经习惯用Java语言编程的程序员来说,有这么几本书带领自己从熟悉的领域进入不熟悉的领域总是件好事。 这几本书中,最深入JVM内部的是《Oracle JRockit: The Definitive Guide》;有丰富调优建议的是《Java Performance》;结合实现大概介绍JVM的抽象概念的是周志明的《深入理解Java虚拟机》。
  4. 虚拟机的入门级实现 先通过《Language Implementation Patterns》了解编程语言的一些入门级实现方式,把高级语言编译器与虚拟机两个概念联系起来。 然后通过《プログラミング言語を作る》了解非常简易的、用树遍历式以及字节码式解释器实现虚拟机大概是个怎么回事。虽然这本书没有实现JVM,但它介绍的Diksam与早期JVM的实现颇有相似之处,可参考。 接下来《深入嵌入式Java虚拟机》介绍了一种实际的JVM——KVM的实现细节。KVM是CLDC的参考实现(RI)里的JVM,结构简单,资源消耗小,适合入门阅读。 这部分最后是《The School of Niklaus Wirth》,里面有一章介绍了HotSpot Client Compiler (C1)的原始设计思路。这是个非常简单、但相对来说性能还不错的JIT编译器,可用于对JIT编译器的基本了解。这本书本身就很赞,不为学习虚拟机也可以一读。 需要注意的是从“简易的JVM实现”到“高性能、复杂的JVM实现”跨度非常大;前者的许多部分的实现方式与后者相当不同。先从简易的实现开始入手主要是为了对JVM里大概都有些什么组件有所了解。但如果目标是了解高性能JVM的实现,那就必须在GC、编译原理方面打下更好的基础,重新洗一次脑。
  5. C++基础书 下面要开始逐渐深入JVM的内部实现,如果没有良好的C或(与?)C++基础会比较吃力。虽然也有几乎完全用Java语言实现的高性能JVM,例如Maxine VM与Jikes RVM,但它们都是研究性质的;商用JVM实现仍然是C与C++的天下。 这里我先推荐C++之父自己写的那本书来入门。虽然BS巨巨后来还出过本新书,而近来也渐渐开始有介绍C++11的入门书,但实际上现在多数JVM实现用的还是C99或非常古老的C++(连C++03都不一定用到了),所以用这本老书应该就够了。 然后通过《深度探索C++对象模型》来学习C++对象模型的常见实现方式。这对后面理解Java对象模型的实现很有帮助。
  6. GC与编译原理的入门书 GC书总共就那么几本,倒也没啥可挑的。《The Garbage Collection Handbook》是绝对必读。 编译原理的书就稍微尴尬些。现有的编译原理书大都针对静态编译器、针对像C或C++那样的偏静态、偏native的语言。我还没读到过什么编译原理书是专门介绍JIT编译器或者说动态编译器的。静态与动态编译器会有些取舍上、实现策略上的差异,不过还好其核心的原理都是一样的,所以还是可以推荐几本书。龙书用来最初入门,鲸书用来补充一些优化相关的知识,EAC第二版用来学习编译器一种比较良好的逻辑组织方式,最后学一下针对现代机器的优化。
  7. 介绍计算机体系结构的书 实际JVM实现里,如果有JIT编译器或者动态编译器那它们的编译目标多半是底层机器的机器码。这就涉及到计算机体系结构了。 如果您只对Java语言和抽象的JVM有一定了解,那可以用《计算机组成及汇编语言原理》来入门。这本书比较奇葩,用JVM的字节码指令集来当作真实机器介绍体系结构的概念。我并不太喜欢这本书,但感觉它对有Java背景的初学者来说应该有点用。要注意的是千万别只读这本书来入门,请结合下面要介绍的一本书来重新洗一次脑。 如果对C或C++已经有所了解,那《深入理解计算机系统》(CSAPP)是计算机体系结构入门的最适合的书了。
  8. 进一步阅读 到此为止各种抽象概念应该都了解得差不多了。那么要在真实的机器上实现高性能JVM,就必须要对真实机器的指令集细节有所了解。x86/x86-64、SPARC、ARM、MIPS,要在哪个平台上做高性能实现就要学习哪个平台的指令集及指令级别优化技巧。这里就不具体推荐书了。 操作系统层面的知识同样重要。像是说JVM要实现线程、内存分配啥的,都可能要跟系统调用或CRT对系统调用的包装打交道。这部分也需要另外找书来读。我回头再考虑下要不要加几本道这个豆列里来。 另外,从80年代开始高级语言虚拟机的实现技术有了突飞猛进的发展,但却没有专门的书对这个领域做综述和导读。多数有用的资料其实还是在论文里。光靠读书是远远不够用的,论文这块也请关注。

    顺便广告一下:我的博客里关于虚拟机的文章也推荐给大家参考:http://rednaxelafx.iteye.com/blog/362738

    这个豆列没有漏掉 《Inside the Java Virtual Machine, Second Edition》 ,中文版《深入Java虚拟机(原书第2版)》,只是我现在已经不再推荐它。这本书刚出版的时候确实引起了一番学习Java虚拟机的热潮,但其部分内容从现在的角度看已经过时,特别是涉及JVM实现的部分。像火车算法什么的现在已经没有JVM实现使用。不过话说回来,了解了解这些过时的信息也没什么不好,前提是能自己分辨清楚哪些信息是适用于现在的JVM的,而哪些已经成为了历史。

概要设计文档模板

Posted on

概要设计文档模板

campo 态度和细节决定一切 专注 务实 探索 随笔- 55 文章- 2 评论- 151

博客园 首页 新随笔 联系 管理 订阅 订阅

概要设计文档模板

概要设计说明书 一. 引言 1. 编写目的 500){this.resized=true;this.style.width=500;}" resized="0">从该阶段开发正式进入软件的实际开发阶段,本阶段完成系统的大致设计并明确系统的数据结构与软件结构。在软件设计阶段主要是把一个软件需求转化为软件表示的过程,这种表示只是描绘出软件的总的概貌。本概要设计说明书的目的就是进一步细化软件设计阶段得出的软件总体概貌,把它加工成在程序细节上非常接近于源程序的软件表示。 2. 项目背景(略) 3. 定义 500){this.resized=true;this.style.width=500;}" resized="0">在该概要设计说明书中的专门术语有: 500){this.resized=true;this.style.width=500;}" resized="0">总体设计**500){this.resized=true;this.style.width=500;}" resized="0">接口设计**

500){this.resized=true;this.style.width=500;}" resized="0">数据结构设计

500){this.resized=true;this.style.width=500;}" resized="0">运行设计

500){this.resized=true;this.style.width=500;}" resized="0">出错设计 500){this.resized=true;this.style.width=500;}" resized="0">具体的概念与含义在文档后将会解释。 4. 参考资料 500){this.resized=true;this.style.width=500;}" resized="0"><软件工程概论> 李存珠 李宣东 编著 南京大学计算机系出版 2001年8月 二. 任务概述 1. 目标 500){this.resized=true;this.style.width=500;}" resized="0">该阶段目的在于明确系统的数据结构和软件结构,此外总体设计还将给出内部软件和外部系统部件之间的接口定义,各个软件模块的功能说明,数据结构的细节以及具体的装配要求。 2. 运行环境 500){this.resized=true;this.style.width=500;}" resized="0">软件基本运行环境为Windows环境。 3. 需求概述(略) 4. 条件与限制 500){this.resized=true;this.style.width=500;}" resized="0">为了评价该设计阶段的设计表示的“优劣程度”,必须遵循以下几个准则: 500){this.resized=true;this.style.width=500;}" resized="0">1.软件设计应当表现出层次结构,它应巧妙地利用各个软件部件之间的控制关系。 500){this.resized=true;this.style.width=500;}" resized="0">2.设计应当是模块化的,即该软件应当从逻辑上被划分成多个部件,分别实现各种特定功能和子功能。 500){this.resized=true;this.style.width=500;}" resized="0">3.设计最终应当给出具体的模块(例如子程序或过程),这些模块就具有独立的功能特性。 500){this.resized=true;this.style.width=500;}" resized="0">4.应当应用在软件需求分析期间得到的信息,采取循环反复的方法来获得设计。 三. 总体设计 1.处理流程 500){this.resized=true;this.style.width=500;}" resized="0">系统的总体处理数据流程如下图: 500){this.resized=true;this.style.width=500;}" resized="0"> 图八500){this.resized=true;this.style.width=500;}" resized="0">总体处理流程图 2.总体结构和模块外部设计 500){this.resized=true;this.style.width=500;}" resized="0">模块是软件结构的基础,软件结构的好坏完全由模块的属性体现出来,把软件模块化的目的是为了降低软件复杂性,使软件设计,测试,调试,维护等工作变得简易,但随着模块数目的增加,通过接口连接这些模块的工作量也随之增加。从这些特性可得出如图九的一条总的成本(或工作量)曲线,在考虑模块化时,应尽量使模块数接近于图中的M,它使得研制成本最小,而且应尽量避免不足的模块化或超量。 500){this.resized=true;this.style.width=500;}" resized="0"> 图九500){this.resized=true;this.style.width=500;}" resized="0">模块化与总体成本 3.功能分配 500){this.resized=true;this.style.width=500;}" resized="0">从程序的结构中可以看出,学生的信息输入输出功能是由学生管理系统进行的。课程的信息输入输出是由课程管理系统进行的,而班级的信息流动则是班级管理系统进行的。 四. 接口设计 500){this.resized=true;this.style.width=500;}" resized="0">由于系统的各种内外部接口是通过借助数据库开发软件来实现的,是完全在数据库内部操作的,故在此略过此内容。 1. 外部接口(略) 2. 内部接口(略) 五. 数据结构设计 1. 逻辑结构设计 student_Info 学生基本信息表 列名 数据类型 可否为空 说明 student_ID INT(4) NOT NULL 学生学号(主键) student_Name CHAR(10) NULL 学生姓名 student_Gender CHAR(2) NULL 学生性别 born_Date DATETIME(8) NULL 出生日期 class_No INT(4) NULL 班号 tele_Number CHAR(10) NULL 联系电话 ru_Date DATETIME(8) NULL 入校时间 address VARCHAR(50) NULL 家庭住址 comment VARCHAR(200) NULL 注释 class_Info 班级信息表格 列名 数据类型 可否为空 说明 class_No INT(4) NOT NULL 班号(主键) grade CHAR(10) NULL 年级 Director CHAR(10) NULL 班主任 Classroom_No CHAR(10) NULL 教室 course_Info 课程基本信息表 列名 数据类型 可否为空 说明 course_No INT(4) NOT NULL 课程编号(主键) course_Name CHAR(10) NULL 课程名称 course_Type CHAR(10) NULL 课程类型 course_Des CHAR(50) NULL 课程描述 gradecourse_Info 年级课程设置表 列名 数据类型 可否为空 说明 grade CHAR(10) NULL 年级 course_Name CHAR(10) NULL 课程名称 result_Info 学生成绩信息表 列名 数据类型 可否为空 说明 exam_No CHAR(10) NOT NULL 考试编号 student_ID INT(4) NOT NULL 学生学号 student_Name CHAR(10) NULL 学生姓名 class_No INT(4) NULL 学生班号 course_Name CHAR(10) NULL 课程名称 result FLOAT(8) NULL 分数 user_Info 系统用户表 列名 数据类型 可否为空 说明 user_ID CHAR(10) NOT NULL 用户名称(主键) user_PWD CHAR(10) NULL 用户密码 user_DES CHAR(10) NULL 用户描述 图十500){this.resized=true;this.style.width=500;}" resized="0">数据库逻辑结构图表 2. 物理结构设计 500){this.resized=true;this.style.width=500;}" resized="0">系统的物理结构具体由数据库来设计与生成,此处略。 3. 数据结构与程序的关系 500){this.resized=true;this.style.width=500;}" resized="0">系统的数据结构由标准数据库语言SQL生成。 500){this.resized=true;this.style.width=500;}" resized="0">具体的例如创建系统用户表格 user_Info的程序用SQL表示就是: 500){this.resized=true;this.style.width=500;}" resized="0">CREATE TABLE[dbo].user_Info500){this.resized=true;this.style.width=500;}" resized="0">500){this.resized=true;this.style.width=500;}" resized="0">[user_ID]charCOLLATE Chinese_PRC_CI_AS NOT NULL, 500){this.resized=true;this.style.width=500;}" resized="0">500){this.resized=true;this.style.width=500;}" resized="0">[user_PWD]charCOLLATE Chinese_PRC_CI_AS NULL, 500){this.resized=true;this.style.width=500;}" resized="0">500){this.resized=true;this.style.width=500;}" resized="0">[user_Des]charCOLLATE Chinese_PRC_CI_AS NULL 500){this.resized=true;this.style.width=500;}" resized="0">) ON [PRIMARY] 六. 运行设计 1. 运行模块的组合 500){this.resized=true;this.style.width=500;}" resized="0">具体软件的运行模块组合为程序多窗口的运行环境,各个模块在软件运行过程中能较好的交换信息,处理数据。 2. 运行控制 500){this.resized=true;this.style.width=500;}" resized="0">软件运行时有较友好的界面,基本能够实现用户的数据处理要求。 3. 运行时间 500){this.resized=true;this.style.width=500;}" resized="0">系统的运行时间基本可以达到用户所提出的要求。 七. 出错处理设计 1. 出错输出信息 500){this.resized=true;this.style.width=500;}" resized="0">在用户使用错误的数据或访问没有权限的数据后,系统给出提示:“对不起,你非法使用数据,没有权限!”而且用户的密码管理可以允许用户修改自己的密码,不允许用户的匿名登录。 2. 出错处理对策 500){this.resized=true;this.style.width=500;}" resized="0">由于数据在数据库中已经有备份,故在系统出错后可以依靠数据库的恢复功能,并且依靠日志文件使系统再启动,就算系统崩溃用户数据也不会丢失或遭到破坏。但有可能占用更多的数据存储空间,权衡措施由用户来决定。 八. 安全保密设计 500){this.resized=true;this.style.width=500;}" resized="0">系统的系统用户管理保证了只有授权的用户才能进入系统进行数据操作,而且对一些重要数据,系统设置为只有更高权限的人员方可读取或是操作。系统安全保密性较高。 九. 维护设计 500){this.resized=true;this.style.width=500;}" resized="0">由于系统较小没有外加维护模块,因为维护工作比较简单,仅靠数据库的一些基本维护

0

0 0

(请您对文章做出评价) «上一篇:windows文章串联 »下一篇:概要设计模板

posted @ 2007-08-24 10:33 campo 阅读(6961) 评论(3) 编辑 收藏 网摘

发表评论

1600226 回复 引用 /#1楼2007-12-24 20:52 | fghfdsgh[未注册用户]

gsdfgdf ;asdlf laji

回复 引用 /#2楼2009-07-30 14:58 | chris.wang[未注册用户]

十分感谢您发布的概要设计文档,对我的工作提供了很大的帮助。 祝您工作顺利! 回复 引用 /#3楼2009-07-30 14:58 | chris.wang[未注册用户]

十分感谢您发布的概要设计文档,对我的工作提供了很大的帮助。 祝您工作顺利! 注册用户登录后才能发表评论,请 登录注册

博客园首页 IT新闻 闪存 知识库 招聘

IT新闻: · 给Oracle支招:改善Java的15种方式 · 谷歌中国面临人才流失 猎头公司挖走多名员工 · 惠普正式开放新加坡云计算实验室 · 微软向美国联邦政府提供云计算服务 · 中国3G运营商 真正的大考才刚开始 每天10分钟,轻松学英语 专题:iPad jQuery Windows 7

网站导航: 博客园首页 IT新闻 个人主页 闪存 程序员招聘 社区 博问 网摘 China-pub 计算机图书网上专卖店!6.5万品种2-8折! China-Pub 计算机绝版图书按需印刷服务

在知识库中查看: 概要设计文档模板

Copyright ©2010 campo

我的主页 个人资料 我的闪存 发短消息

< 2007年8月 > 日 一 二 三 四 五 六 29 30 31 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 1 2 3 4 5 6 7 8

搜索

常用链接

随笔分类

随笔档案

文章分类

相册

编程技术文章

1. Re:概要设计文档模板

十分感谢您发布的概要设计文档,对我的工作提供了很大的帮助。祝您工作顺利! (chris.wang) 2. Re:概要设计文档模板

十分感谢您发布的概要设计文档,对我的工作提供了很大的帮助。祝您工作顺利! (chris.wang) 3. re: 2007IT业薪资调查,请注明城市

4000 应届 北京 (飞林沙) 4. re: 2007IT业薪资调查,请注明城市

1500 一年 青岛 唉 (随&风) 5. re: 2007IT业薪资调查,请注明城市

6K-8K。 (养猪设备)

阅读排行榜

msn:campolake@hotmail.com