Java架构之JVM调优

  1. JVM Tuning基础知识 1.1 Java堆结构

  Java堆可以处于物理上不连续的内存空间上,只要逻辑上是连续的即可。Java堆就是各种对象分配和保存的内存空间,线程间共享。Java堆分为Eden区,Survivor区,tenured区和Permanent区,如下图所示。

  Java堆的分配原则如下:

Java堆分布如下图所示,新的类的实例大部分在Eden(之所以用Eden这个词也就是表示初创起始的意思)区分配。 Eden区满的时候,或者需要GC时,依然存活的对象将被复制到Survivor区,一共有两个survivor区,当一个survivor区满时,依然存活的对象将被复制到另一个survivor区。 当survivor区全满时,从第一个survivor区复制过来的,且此时还存活的对象将被复制到Tenured Generation(年老代) Perm Generation用于存放静态文件,持久代大小可以通过MaxPermSize进行设置。

Perm Generation是JVM的驻留内存,用于存放JDK自身携带的CLASS,Interface的元数据等。被装载进此区域的数据是不会被垃圾回收器GC回收掉的,关闭JVM时,释放此区域所控制的内存。

Java对象的声明周期在堆中从Young到Tenured,这个期间从Eden诞生到Tenured死亡。当EDEN区不够用时,将触发minor gc,GC会对EDEN区进行垃圾回收,将不再使用的对象进行销毁,同时如果发现对象还被其他对象引用,则将对象移动到survivor区,后面依此类推,当Tenured区发生GC时,称为major gc,由于java中大部分对象的生命周期都很短,所以GC一般发生在Eden区,因此minor gc发生的频率比major gc要高很多。如果最后整个堆空间都满了,则会爆出异常JVM对空间溢出:java.lang.OutOfMemoryError: java heap space。

1.2 JVM GC算法枚举

Mark-sweep算法   即标记回收算法,将需要回收的对象标记,再统一回收。这种算法适合Perm代的对象,以为Perm代的存储空间比较大,需要回收的又不多。

Copying算法   即复制算法,直接拷贝。适合Eden区的对象向survivor区拷贝,因为Eden区的对象大部分都需要GC,而且Eden区的容量小。

Mark-compact算法    即标记整理算法,与标记清除算法不同,标记整理算法避免了内存碎片问题,它将所有存活的对象向内存的一端移动,“整理”完成后,一端是存活的对象一端是可回收内存,然后直接清除可回收内存。 

1.3 GC收集器

Serial收集器

   最基本、最古老的收集器。这是一个单线程收集器,GC过程中,应用会被停掉,即stop-the-world。

Parnew收集器

   实现代码与serial差不多,不同的是这是一个多线程回收器。

Paralled scavenge收集器

   Paralled scavenger收集器是一个新生代回收器,使用复制算法,也是一个多线程并行收集器。parallel scavenge收集器专注于提高吞吐量,吞吐量定义为: 

 吞吐量 = 用户程序执行时间/(用户程序执行时间 + 垃圾回收时间)

Serial old收集器

   Serial old是Serial收集器的老年代版本。

Parallel old收集器

   Parallel Scavenge收集器的老年代版本。使用标记-整理算法。

CMS(concurrent mark sweep)收集器:并发标记清除收集器,以最短停顿时间为目标的收集器。采用标记—清除算法实现。 

  2.VisualVM1实际监控JVM状态

2.1 常用调优参数列表

  主要需要熟悉的是总存储空间大小,各个区的比例设置,针对不同的区设定不同的回收器和回收算法。在默认情况下有些选项是不打开的,因此需要手动配置,并且根据自己应用的情况选择适当的参数。       

   2.2 查看并分析GC日志

    对于应用程序,在eclipse中可以通过工程属性增加VM属性,将GC输出保存为日志文件。              

    GC输出选项:

-XX:+PrintGC 输出GC日志     -XX:+PrintGCDetails 输出GC的详细日志

-XX:+PrintGCTimeStamps 输出GC的时间戳(以基准时间的形式)

-XX:+PrintGCDateStamps 输出GC的时间戳(以日期的形式,如 2013-05-04T21:53:59.234+0800)

-XX:+PrintHeapAtGC 在进行GC的前后打印出堆的信息

-Xloggc:../logs/gc.log 日志文件的输出路径

日志输出形式:

编写了一个简单的递归计算Fibbonaccy数列的程序,并在最后增加了一句System.gc()强制进行Full GC,得到的GC日志如下,回收后Eden区使用量为0。当服务器型的应用运行起来时,免不了会有多次GC,通过GC日志可以比较容易定位性能问题,比如full gc次数过多等。

测试环境:

  JVM: Java HotSpot(TM) Client VM (25.60-b23, mixed mode, sharing)   Java: version 1.8.0_60, vendor Oracle Corporation

Step1:

监控界面,能够直观地看到JVM各个区域的内存使用情况。除了监视虚拟机,还可以监视应用的内存使用情况。尝试对Eclipse的启动进行优化,可以很明显的看到eclipse启动时,S0的对象向S1拷贝,而old区的使用量略微增加。

还可以监控当前CPU和内存负载情况,类加载数量和线程数:

Step2:

eclipse安装目录下有个.ini文件,即eclipse的配置文件,将需要的参数配置填写进去即可。启动eclipse后,查看GC日志,发现整个启动过程中共发生了27次GC,其中有3次Full GC,整个启动过程,我掐指一算大概6~7秒,相当慢了

通过参数-XX:NewSize=n 将Eden区重设为128m后,再次启动eclipse发现,这次就只发生了11次GC,下降了一般还多,启动速度也明显比原来快了一些。

Step3:

接着使用-Xverify:none禁掉类加载时的字节码验证过程,GC次数为10次,现在启动速度基本为3秒多左右,比原来快了一倍。

然而这种调优粒度太粗,服务器上程序需要定位更精确的时间,所以需要更多参数来调试。

  先写到这里吧。    

  

转载于:https://juejin.im/post/5d0725cc5188255fc638480e

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值