一次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 常用调优参数列表
主要需要熟悉的是总存储空间大小,各个区的比例设置,针对不同的区设定不同的回收器和回收算法。在默认情况下有些选项是不打开的,因此需要手动配置,并且根据自己应用的情况选择适当的参数。
| 参数 | 描述 |
|
–XX:+UseSerialGC |
使用串行GC收集器 |
|
–XX:+UseParallelGC |
使用并行GC收集器 |
|
–XX:+UseParallelOldGC |
使用parallel old收集器 |
| –XX:+UseConcMarkSweepGC | 使用CMS收集器 |
| -Xms | 初始状态堆大小 |
| -Xmx | 堆的最大大小 |
| -XX:MaxPermSize=n | 设置Permanent区的大小 |
| -XX:NewSize=n | 设置Young generation大小 |
| -XX:NewRatio=n | 设置年轻代和年老代的比值 |
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 日志文件的输出路径
日志输出形式:

Java HotSpot(TM) Client VM (25.45-b02) for windows-x86 JRE (1.8.0_45-b14), built on Apr 10 2015 10:46:40 by "java_re" with MS VC++ 10.0 (VS2010)
Memory: 4k page, physical 2074576k(402044k free), swap 4149152k(1441572k free)
CommandLine flags: -XX:InitialHeapSize=16777216 -XX:MaxHeapSize=268435456 -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:-UseLargePagesIndividualAllocation
Heap
def new generation total 4928K, used 885K [0x04600000, 0x04b50000, 0x09b50000)
eden space 4416K, 20% used [0x04600000, 0x046dd5f0, 0x04a50000)
from space 512K, 0% used [0x04a50000, 0x04a50000, 0x04ad0000)
to space 512K, 0% used [0x04ad0000, 0x04ad0000, 0x04b50000)
tenured generation total 10944K, used 0K [0x09b50000, 0x0a600000, 0x14600000)
the space 10944K, 0% used [0x09b50000, 0x09b50000, 0x09b50200, 0x0a600000)
Metaspace used 98K, capacity 2242K, committed 2368K, reserved 4480K

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

{Heap before GC invocations=0 (full 0):
def new generation total 4928K, used 1522K [0x04600000, 0x04b50000, 0x09b50000)
eden space 4416K, 34% used [0x04600000, 0x0477c9e8, 0x04a50000)
from space 512K, 0% used [0x04a50000, 0x04a50000, 0x04ad0000)
to space 512K, 0% used [0x04ad0000, 0x04ad0000, 0x04b50000)
tenured generation total 10944K, used 0K [0x09b50000, 0x0a600000, 0x14600000)
the space 10944K, 0% used [0x09b50000, 0x09b50000, 0x09b50200, 0x0a600000)
Metaspace used 259K, capacity 2280K, committed 2368K, reserved 4480K
15.463: [Full GC (System.gc()) 15.477: [Tenured: 0K->776K(10944K), 0.0428098 secs] 1522K->776K(15872K), [Metaspace: 259K->259K(4480K)], 0.0688257 secs] [Times: user=0.02 sys=0.00, real=0.07 secs]
Heap after GC invocations=1 (full 1):
def new generation total 4992K, used 0K [0x04600000, 0x04b60000, 0x09b50000)
eden space 4480K, 0% used [0x04600000, 0x04600000, 0x04a60000)
from space 512K, 0% used [0x04a60000, 0x04a60000, 0x04ae0000)
to space 512K, 0% used [0x04ae0000, 0x04ae0000, 0x04b60000)
tenured generation total 10944K, used 776K [0x09b50000, 0x0a600000, 0x14600000)
the space 10944K, 7% used [0x09b50000, 0x09c122f0, 0x09c12400, 0x0a600000)
Metaspace used 259K, capacity 2280K, committed 2368K, reserved 4480K
}
Heap
def new generation total 4992K, used 45K [0x04600000, 0x04b60000, 0x09b50000)
eden space 4480K, 1% used [0x04600000, 0x0460b4a8, 0x04a60000)
from space 512K, 0% used [0x04a60000, 0x04a60000, 0x04ae0000)
to space 512K, 0% used [0x04ae0000, 0x04ae0000, 0x04b60000)
tenured generation total 10944K, used 776K [0x09b50000, 0x0a600000, 0x14600000)
the space 10944K, 7% used [0x09b50000, 0x09c122f0, 0x09c12400, 0x0a600000)
Metaspace used 259K, capacity 2280K, committed 2368K, reserved 4480K

测试环境:
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秒多左右,比原来快了一倍。

{Heap before GC invocations=10 (full 4):
def new generation total 36864K, used 35041K [0x04400000, 0x06bf0000, 0x0eea0000)
eden space 32832K, 100% used [0x04400000, 0x06410000, 0x06410000)
from space 4032K, 54% used [0x06410000, 0x06638570, 0x06800000)
to space 4032K, 0% used [0x06800000, 0x06800000, 0x06bf0000)
tenured generation total 57224K, used 40267K [0x0eea0000, 0x12682000, 0x24400000)
the space 57224K, 70% used [0x0eea0000, 0x115f2ca0, 0x115f2e00, 0x12682000)
Metaspace used 31836K, capacity 33329K, committed 33408K, reserved 34176K
5.742: [GC (Allocation Failure) 5.742: [DefNew: 35041K->4031K(36864K), 0.0193873 secs] 75308K->46613K(94088K), 0.0194649 secs] [Times: user=0.02 sys=0.00, real=0.02 secs]

然而这种调优粒度太粗,服务器上程序需要定位更精确的时间,所以需要更多参数来调试。
先写到这里吧。
玄不救非,氪不改命。
Reference
1.http://timyang.net/java/java_gc_tunning/
一次JVM调优的笔记的更多相关文章
- JVM调优- 学习笔记(转)
http://blog.csdn.net/fenglibing/article/details/6321453 GC学习笔记 这是我公司同事的GC学习笔记,写得蛮详细的,由浅入深,循序渐进,让人一看就 ...
- JVM底层原理及调优之笔记一
JVM底层原理及调优 1.java虚拟机内存模型(JVM内存模型) 1.堆(-Xms -Xmx -Xmn) java堆,也称为GC堆,是JVM中所管理的内存中最大的一块内存区域,是线程共享的,在JVM ...
- java虚拟机学习-JVM调优总结-分代垃圾回收详述(9)
为什么要分代 分代的垃圾回收策略,是基于这样一个事实:不同的对象的生命周期是不一样的.因此,不同生命周期的对象可以采取不同的收集方式,以便提高回收效率. 在Java程序运行的过程中,会产生大量的对象, ...
- JVM 调优之 Eclipse 启动调优实战
本文是我12年在学习<深入理解Java虚拟机:JVM高级特性与最佳实践>时,做的一个 JVM 简单调优实战笔记,版本都有些过时,不过调优思路和过程还是可以分享给大家参考的. 环境基础配置 ...
- JVM调优篇
点赞再看,养成习惯,微信搜索「小大白日志」关注这个搬砖人. 文章不定期同步公众号,还有各种一线大厂面试原题.我的学习系列笔记. 基础概念 一般JVM调优,重点在于调整JVM堆大小.调整垃圾回收器 jv ...
- jvm系列(四):jvm调优-命令大全(jps jstat jmap jhat jstack jinfo)
文章同步发布于github博客地址,阅读效果更佳,欢迎品尝 运用jvm自带的命令可以方便的在生产监控和打印堆栈的日志信息帮忙我们来定位问题!虽然jvm调优成熟的工具已经有很多:jconsole.大名鼎 ...
- jvm系列(六):jvm调优-从eclipse开始
jvm调优-从eclipse开始 概述 什么是jvm调优呢?jvm调优就是根据gc日志分析jvm内存分配.回收的情况来调整各区域内存比例或者gc回收的策略:更深一层就是根据dump出来的内存结构和线程 ...
- JVM调优总结:调优方法
JVM调优总结:调优方法 2012-01-10 14:35 和你在一起 和你在一起的博客 字号:T | T 下面文章将讲解JVM的调优工具以及如何去调优等等问题,还有一些异常问题的处理.详细请看下文. ...
- [转]JVM调优总结:一些概念
JVM调优总结:一些概念 原文出处: pengjiaheng 数据类型 Java虚拟机中,数据类型可以分为两类:基本类型和引用类型.基本类型的变量保存原始值,即:他代表的值就是数值本身:而引用类型的变 ...
随机推荐
- UNet简单案例讲解
1.创建文件夹: 2.创建一个空物体,添加如下组件: Network Manager(网络管理组件): Network Manager HUD(提供一个UI): 3.创建如下模型,并设置为预制体: 给 ...
- web自动化测试:watir+minitest(三)
本文,谢绝转载. 整体框架设计: 1.用例的解耦性.一个测试用例一个脚本.而并非minitest中的N个test写在一个文件中 2.单独调试与全量连跑或部分连跑 3.任意变量.参数配置.这点对后期维护 ...
- Android实现金额显示小数点后两位
代码改变世界 Android实现金额显示小数点后两位 NumberFormat nf = new DecimalFormat("##.##"); Double d = 554545 ...
- 【翻译】Apache软件基金会1
最近有点看不进去书,所以就找点东西翻译下,正好很想了解Apache基金会都有什么开源项目,每天找点事时间翻译翻译,还可以扩展下视野. 今天就看了两个,第一个是关于.NET的,不再兴趣范围内.第二个还挺 ...
- windows 批处理删除指定目录下 指定类型 指定天数之前文件
删除D:\test下5天前所有文件,如下: @echo offset SrcDir=D:\testset DaysAgo=5forfiles /p %SrcDir% /s /m *.* /d -%Da ...
- idea下载多个插件项目启动不了解决方案
今天下载mybatis plugin插件的时候 有好多个版本的plugin,然后呢,看第二个比较热门,就下载了第二个,然后重启idea发现这个插件貌似得花钱,那算了吧,咱用第一个免费的吧,就又下载了第 ...
- Bzoj2882 工艺 [线性算法]
后缀自动机题解 -> http://www.cnblogs.com/SilverNebula/p/6420601.html 后缀自动机敲完,看了下排行,wc为什么别人跑得这么快?……是诶,这最小 ...
- 如何在Linux的桌面上创建快捷方式或启动器
如果在Linux桌面系统中你经常使用一个程序,你可能想去创建一个“桌面快捷方式”,以便于你在桌面只要点击一下快捷方式就可以启动它.虽然不少带有图形界面的程序会在安装时自动在桌面上创建快捷方式,还有一些 ...
- Ubuntu中vim添加lua支持
系统:Ubuntu 15.10/16.04 因为Ubuntu15.10系统自带vim不支持lua,所以得自己编译安装. $ sudo apt install vim-nox vim-nox可以让vim ...
- python-urllib/urllib2模块
urllib与urllib2: urllib2可以接受一个Request类的实例来设置URL请求的headers,urllib仅可以接受URL.这意味着,你不可以伪装你的User Agent字符串等. ...