jvm学习笔记

   1 HotSpot算法实现      

          1 枚举根节点

              作为GC Root 的节点主要在全局性的引用(常量或静态属性)与执行上下文(栈帧的本地变量表),现在仅仅方法区就数百兆,如果逐个检测引用,会消耗很多时间,虚拟机应该有办法知道哪些地方存在对象的引用,在HotSpot中使用OopMap数据结构达到目的,在类加载完后,HotSpot就把对象什么偏移量是什么类型数据计算出来。

           2 安全点

          HotSpot在特定的位置停下来执行GC,这些位置为安全点,安全点的选择是是否有让程序长时间执行的特征,长时间执行就是指令序列复用,如方法调用,循环跳转,异常跳转,具有这些功能的指令才产生safepoint ,当发生GC时,如何让线程在安全点上停下来呢?抢占式中断:中断所有线程,如果没有到安全点,让线程跑到安全点,主动中断:GC时设置标志位,线程主动轮询标志位

     2 虚拟机参数配置

       虚拟机参数有3种表示方法        http://www.cnblogs.com/wenfeng762/archive/2011/08/14/2137810.html

        1 标准参数(-),所有的JVM实现都必须实现这些参数的功能,而且向后兼容;

         2 非标准参数(-X),默认jvm实现这些参数的功能,但是并不保证所有jvm实现都满足,且不保证向后兼容;

        3 非稳定参数(-XX),此类参数各个jvm实现会有所不同,将来可能会随时取消,需要慎重使用

     3 标准参数

           其实标准参数是用过Java的人都最熟悉的,就是你在运行java命令时后面加上的参数,如java -version, java -jar 等,输入命令java -help或java -?就能获得当前机器所有java的标准参数列表。

           -client
       设置jvm使用client模式,这是一般在pc机器上使用的模式,启动很快,但性能和内存管理效率并不高;多用于桌面应用;

      server
使用server模式,启动速度虽然慢(比client模式慢10%左右),但是性能和内存管理效率很高,适用于服务器,用于生成环境、开发环境或测试环境的服务端;

     -classpath / -cp
JVM加载和搜索文件的目录路径,多个路径用;分隔。注意,如果使用了-classpath,JVM就不会再搜索环境变量中定义的CLASSPATH路径。
JVM搜索路径的顺序为:
1,先搜索JVM自带的jar或zip包(Bootstrat,搜索路径可以用System.getProperty("sun.boot.class.path")获得);
2,搜索JRE_HOME/lib/ext下的jar包(Extension,搜索路径可以用System.getProperty("java.ext.dirs")获得);
3,搜索用户自定义目录,顺序为:当前目录(.),CLASSPATH,-cp;(搜索路径用System.getProperty("java.class.path")获得)

     -DpropertyName=value
定义系统的全局属性值,如配置文件地址等,如果value有空格,可以用-Dname="space string"这样的形式来定义,用System.getProperty("propertyName")可以获得这些定义的属性值,在代码中也可以用System.setProperty("propertyName","value")的形式来定义属性。

-verbose
这是查询GC问题最常用的命令之一,具体参数如:
-verbose:class
 输出jvm载入类的相关信息,当jvm报告说找不到类或者类冲突时可此进行诊断。
-verbose:gc
 输出每次GC的相关情况,后面会有更详细的介绍。
-verbose:jni
 输出native方法调用的相关情况,一般用于诊断jni调用错误信息。

         非标准参数

         非标准参数,是在标准参数的基础上进行扩展的参数,输入“java -X”命令,能够获得当前JVM支持的所有非标准参数列表(你会发现,其实并不多哦)。

        -Xmn
新生代内存大小的最大值,包括E区和两个S区的总和,使用方法如:-Xmn65535,-Xmn1024k,-Xmn512m,-Xmn1g (-Xms,-Xmx也是种写法)

            -Xms
初始堆的大小,也是堆大小的最小值,默认值是总共的物理内存/64(且小于1G)

             -Xmx
堆的最大值,默认值是总共的物理内存/64(且小于1G)整个堆的大小=年轻代大小+年老代大小,堆的大小不包含持久代大小,如果增大了年轻代,年老代相应就会减小,官方默认的配置为年老代大小/年轻代大小=2/1左右(使用-XX:NewRatio可以设置-XX:NewRatio=5,表示年老代/年轻代=5/1)

      -Xss
这个参数用于设置每个线程的栈内存,默认1M,一般来说是不需要改的。

 -Xrs
减少JVM对操作系统信号(OS Signals)的使用

-Xprof
 跟踪正运行的程序,并将跟踪数据在标准输出输出;适合于开发环境调试。

-Xnoclassgc
 关闭针对class的gc功能;因为其阻止内存回收,所以可能会导致OutOfMemoryError错误,慎用;

-Xincgc
 开启增量gc(默认为关闭);这有助于减少长时间GC时应用程序出现的停顿;但由于可能和应用程序并发执行,所以会降低CPU对应用的处理能力。

-Xloggc:file
 与-verbose:gc功能类似,只是将每次GC事件的相关情况记录到一个文件中,文件的位置最好在本地,以避免网络的潜在问题。
 若与verbose命令同时出现在命令行中,则以-Xloggc为准。

        非Stable参数(非静态参数)

以-XX表示的非Stable参数,虽然在官方文档中是不确定的,不健壮的,各个公司的实现也各有不同,但往往非常实用,所以这部分参数对于GC非常重要。JVM(Hotspot)中主要的参数可以大致分为3类(参考http://blog.csdn.net/sfdev/article/details/2063928):

  • 性能参数( Performance Options):用于JVM的性能调优和内存分配控制,如初始化内存大小的设置;
  • 行为参数(Behavioral Options):用于改变JVM的基础行为,如GC的方式和算法的选择;
  • 调试参数(Debugging Options):用于监控、打印、输出等jvm参数,用于显示jvm更加详细的信息;
  • -XX:+<option> 启用选项
  • -XX:-<option> 不启用选项
  • -XX:<option>=<number> 给选项设置一个数字类型值,可跟单位,例如 32k, 1024m, 2g
  • -XX:<option>=<string> 给选项设置一个字符串值,例如-XX:HeapDumpPath=./dump.core
收集器搭配

       

Serial收集器: Serial收集器是在client模式下默认的新生代收集器,其收集效率大约是100M左右的内存需要几十到100多毫秒;在client模式下,收集桌面应用的内存垃圾,基本上不影响用户体验。所以,一般的Java桌面应用中,直接使用Serial收集器(不需要配置参数,用默认即可)。
ParNew收集器:Serial收集器的多线程版本,这种收集器默认开通的线程数与CPU数量相同,-XX:ParallelGCThreads可以用来设置开通的线程数。
可以与CMS收集器配合使用,事实上用-XX:+UseConcMarkSweepGC选择使用CMS收集器时,默认使用的就是ParNew收集器,所以不需要额外设置-XX:+UseParNewGC,设置了也不会冲突,因为会将ParNew+Serial Old作为一个备选方案;
如果单独使用-XX:+UseParNewGC参数,则选择的是ParNew+Serial Old收集器组合收集器。
一般情况下,在server模式下,如果选择CMS收集器,则优先选择ParNew收集器。
Parallel Scavenge收集器:关注的是吞吐量(关于吞吐量的含义见上一篇博客),可以这么理解,关注吞吐量,意味着强调任务更快的完成,而如CMS等关注停顿时间短的收集器,强调的是用户交互体验。
在需要关注吞吐量的场合,比如数据运算服务器等,就可以使用Parallel Scavenge收集器。

老年代收集器如下:
Serial Old收集器:在1.5版本及以前可以与 Parallel Scavenge结合使用(事实上,也是当时Parallel Scavenge唯一能用的版本),另外就是在使用CMS收集器时的备用方案,发生 Concurrent Mode Failure时使用。
如果是单独使用,Serial Old一般用在client模式中。
Parallel Old收集器:在1.6版本之后,与 Parallel Scavenge结合使用,以更好的贯彻吞吐量优先的思想,如果是关注吞吐量的服务器,建议使用Parallel Scavenge + Parallel Old 收集器。
CMS收集器:这是当前阶段使用很广的一种收集器,国内很多大的互联网公司线上服务器都使用这种垃圾收集器(http://blog.csdn.net/wisgood/article/details/17067203),笔者公司的收集器也是这种,CMS收集器以获取最短回收停顿时间为目标,非常适合对用户响应比较高的B/S架构服务器。
 CMSIncrementalMode: CMS收集器变种,属增量式垃圾收集器,在并发标记和并发清理时交替运行垃圾收集器和用户线程。
 G1 收集器:面向服务器端应用的垃圾收集器,计划未来替代CMS收集器。

  • 一般来说,如果是Java桌面应用,建议采用Serial+Serial Old收集器组合,即:-XX:+UseSerialGC(-client下的默认参数)
  • 在开发/测试环境,可以采用默认参数,即采用Parallel Scavenge+Serial Old收集器组合,即:-XX:+UseParallelGC(-server下的默认参数)
  • 在线上运算优先的环境,建议采用Parallel Scavenge+Serial Old收集器组合,即:-XX:+UseParallelGC
  • 在线上服务响应优先的环境,建议采用ParNew+CMS+Serial Old收集器组合,即:-XX:+UseConcMarkSweepGC

另外在选择了垃圾收集器组合之后,还要配置一些辅助参数,以保证收集器可以更好的工作。关于这些参数,请在http://kenwublog.com/docs/java6-jvm-options-chinese-edition.htm中查询其意义和用法,如:

  • 选用了ParNew收集器,你可能需要配置4个参数: -XX:SurvivorRatio, -XX:PretenureSizeThreshold, -XX:+HandlePromotionFailure,-XX:MaxTenuringThreshold;
  • 选用了 Parallel Scavenge收集器,你可能需要配置3个参数: -XX:MaxGCPauseMillis,-XX:GCTimeRatio, -XX:+UseAdaptiveSizePolicy ;
  • 选用了CMS收集器,你可能需要配置3个参数: -XX:CMSInitiatingOccupancyFraction, -XX:+UseCMSCompactAtFullCollection, -XX:CMSFullGCsBeforeCompaction;
启动内存分配

关于GC有一个常见的疑问是,在启动时,我的内存如何分配?经过前面的学习,已经很容易知道,用-Xmn,-Xmx,-Xms,-Xss,-XX:NewSize,-XX:MaxNewSize,-XX:MaxPermSize,-XX:PermSize,-XX:SurvivorRatio,-XX:PretenureSizeThreshold,-XX:MaxTenuringThreshold就基本可以配置内存启动时的分配情况。但是,具体配置多少?设置小了,频繁GC(甚至内存溢出),设置大了,内存浪费。结合前面对于内存区域和其作用的学习,尽量考虑如下建议:

  1. -XX:PermSize尽量比-XX:MaxPermSize小,-XX:MaxPermSize>= 2 * -XX:PermSize, -XX:PermSize> 64m,一般对于4G内存的机器,-XX:MaxPermSize不会超过256m;
  2. -Xms =  -Xmx(线上Server模式),以防止抖动,大小受操作系统和内存大小限制,如果是32位系统,则一般-Xms设置为1g-2g(假设有4g内存),在64位系统上,没有限制,不过一般为机器最大内存的一半左右;
  3. -Xmn,在开发环境下,可以用-XX:NewSize和-XX:MaxNewSize来设置新生代的大小(-XX:NewSize<=-XX:MaxNewSize),在生产环境,建议只设置-Xmn,一般-Xmn的大小是-Xms的1/2左右,不要设置的过大或过小,过大导致老年代变小,频繁Full GC,过小导致minor GC频繁。如果不设置-Xmn,可以采用-XX:NewRatio=2来设置,也是一样的效果;
  4. -Xss一般是不需要改的,默认值即可。
  5. -XX:SurvivorRatio一般设置8-10左右,推荐设置为10,也即:Survivor区的大小是Eden区的1/10,一般来说,普通的Java程序应用,一次minorGC后,至少98%-99%的对象,都会消亡,所以,survivor区设置为Eden区的1/10左右,能使Survivor区容纳下10-20次的minor GC才满,然后再进入老年代,这个与 -XX:MaxTenuringThreshold的默认值15次也相匹配的。如果XX:SurvivorRatio设置的太小,会导致本来能通过minor回收掉的对象提前进入老年代,产生不必要的full gc;如果XX:SurvivorRatio设置的太大,会导致Eden区相应的被压缩。
  6. -XX:MaxTenuringThreshold默认为15,也就是说,经过15次Survivor轮换(即15次minor GC),就进入老年代, 如果设置的小的话,则年轻代对象在survivor中存活的时间减小,提前进入年老代,对于年老代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象在年轻代的存活时间,增加在年轻代即被回收的概率。需要注意的是,设置了 -XX:MaxTenuringThreshold,并不代表着,对象一定在年轻代存活15次才被晋升进入老年代,它只是一个最大值,事实上,存在一个动态计算机制,计算每次晋入老年代的阈值,取阈值和MaxTenuringThreshold中较小的一个为准。
  7. -XX:PretenureSizeThreshold一般采用默认值即可。
监控工具和方法

          在jvm运行过程中,为保证稳定,高效,或在出现GC问题时分析问题原因,需要对GC进行检控,分析当前GC情况,鉴别jvm是否高效进行垃圾回收,有无必要调优。

1,minor GC和full GC的频率;
2,执行一次GC所消耗的时间;
3,新生代的对象何时被移到老生代以及花费了多少时间;
4,每次GC中,其它线程暂停(Stop the world)的时间;
5,每次GC的效果如何,是否不理想;
………………
监控GC的工具分为2种:命令行工具和图形工具;

1 jps 用于查询在运行的jvm进程

2 jstat 实时显示本地或远程jvm进程中类装载,内存,垃圾收集,jit编译等数据,

3 jinfo 查询当前运行的jvm属性和参数的值

4 jmap 用于显示当前Java堆和永久代的详细信息(如当前使用的收集器,当前的空间使用率等)

5 jhat 用于分析使用jmap生成的dump文件,是JDK自带的工具,使用方法为: jhat -J -Xmx512m [file]

6 用于生成当前JVM的所有线程快照,线程快照是虚拟机每一条线程正在执行的方法,目的是定位线程出现长时间停顿的原因。

调优方法


一切都是为了这一步,调优,在调优之前,我们需要记住下面的原则:

  1. 多数的Java应用不需要在服务器上进行GC优化;
  2. 多数导致GC问题的Java应用,都不是因为我们参数设置错误,而是代码问题;
  3. 在应用上线之前,先考虑将机器的JVM参数设置到最优(最适合);
  4. 减少创建对象的数量;
  5. 减少使用全局变量和大对象;
  6. GC优化是到最后不得已才采用的手段;
  7. 在实际使用中,分析GC情况优化代码比优化GC参数要多得多;

GC优化的目的有两个(http://www.360doc.com/content/13/0305/10/15643_269388816.shtml):

  • 将转移到老年代的对象数量降低到最小;
  • 减少full GC的执行时间;

为了达到上面的目的,一般地,你需要做的事情有:

  • 减少使用全局变量和大对象;
  • 调整新生代的大小到最合适;
  • 设置老年代的大小为最合适;
  • 选择合适的GC收集器;
1,监控GC的状态
使用各种JVM工具,查看当前日志,分析当前JVM参数设置,并且分析当前堆内存快照和gc日志,根据实际的各区域内存划分和GC执行时间,觉得是否进行优化;
2,分析结果,判断是否需要优化
如果各项参数设置合理,系统没有超时日志出现,GC频率不高,GC耗时不高,那么没有必要进行GC优化;如果GC时间超过1-3秒,或者频繁GC,则必须优化;
注:如果满足下面的指标,则一般不需要进行GC:
  • Minor GC执行时间不到50ms;
  • Minor GC执行不频繁,约10秒一次;
  • Full GC执行时间不到1s;
  • Full GC执行频率不算频繁,不低于10分钟1次;
3,调整GC类型和内存分配
如果内存分配过大或过小,或者采用的GC收集器比较慢,则应该优先调整这些参数,并且先找1台或几台机器进行beta,然后比较优化过的机器和没有优化的机器的性能对比,并有针对性的做出最后选择;
4,不断的分析和调整
通过不断的试验和试错,分析并找到最合适的参数
5,全面应用参数
如果找到了最合适的参数,则将这些参数应用到所有服务器,并进行后续跟踪。

http://www.cnblogs.com/zhguang/p/Java-JVM-GC.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值