1. jetty 修改JVM的参数
deploy/bin/env.sh
在上面的环境变量脚本中进行修改;如果分配给JVM的内存是4g
这个里面的JAVA_OPTS 的配置项就是用来给JVM 进行配置的;
- Xms:初始Head 内存的大小,应用使用的最小内存
- Xmx:Heap 内存的最大值
- -XX:NewSize=1024m -XX:MaxNewSize=1024m:新生代内存的大小,一般分配NewSize是Xms的 1/4 ---1/8
- -XX:PermSize=160m -XX:MaxPermSize=160m :永生代内存的大小,一般是用来加载 静态文件的,只要比最大的静态文件多个10--20% 的bufffer 即可
- -XX:+UseParallelGC :GC的方式选择并行方式,GC 时消耗性能比较少
- -Xloggc:/home/.../logs/gc.log #启动gclog ,会把JVM 每次gc的信息打印到gc.log 里面
其他还有很多变量;java的内存分为 young old perf 当三个内存来回切换频繁是,会多长GC 就是垃圾回收;会影响我们的性能。
2. JVM 参数详解:
http://singleant.iteye.com/blog/1308842
http://aixiangct.blog.163.com/blog/static/915224612010101833230342/
JVM 参数设置经验:
http://www.cnblogs.com/redcreen/archive/2011/05/04/2037057.html
3. JVM 信息查看工具命令
jstat 查看 GC 的信息 【选项:-gcutil】 查看gc的信息
sudo -u admin jstat -gcutil 6245 3000
S0 S1 E O P YGC YGCT FGC FGCT GCT
0.00 21.03 33.28 6.66 62.39 31 0.987 0 0.000 0.987
0.00 13.06 91.34 7.53 62.63 39 1.094 0 0.000 1.094
0.00 13.06 98.36 7.53 62.63 39 1.094 0 0.000 1.094
11.05 0.00 6.25 7.59 62.65 40 1.104 0 0.000 1.104
11.05 0.00 11.10 7.59 62.65 40 1.104 0 0.000 1.104
#s0 s1:是内存的2个交换区,如果内存从E(eden 区)转移到O(old 区)时需要借助S0 S1 2个中间交换区转移。
# E eden :新生代区,java代码中新new的类占用的内存;
# O old :老生代区,当新生代区(E)内存占用100时会把E里面的数据迁移到O;当O里面的数据占用内存超过90%时会出发FullGC;在JVM FullGC 期间,java的服务停止服务(大概持续1s的时间);此时如果收到外部的请求会超时:rt
# P 永久(永生)生代:一般用来加载一些静态数据,java进程启动时会一次性加载,加载之后基本就不变化了。
# YGC :年轻代(新生代) 进行GC,新生代GC 及时回收内存,代价比较小也比较频繁
# YGCT:年轻待GC 累加的时间之和(服务重启归0)
# FGC: 全堆范围内进行GC ,包括YGC 和OGC,每次FGC时java服务暂停,这样代价比较大
# GCT: 所有的gc的占用的时间
jmap 查看堆 heap的内存使用情况
sudo -u admin jmap -heap 6245
Attaching to process ID 6245, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 20.0-b11
using thread-local object allocation.
Parallel GC with 8 thread(s)
Heap Configuration: #JVM 的配置:JAVA_OPTS=" -server
MinHeapFreeRatio = 40
MaxHeapFreeRatio = 70
MaxHeapSize = 5368709120 (5120.0MB)#整个堆内存的大小 -Xms5g -Xmx5g
NewSize = 1073741824 (1024.0MB)#新生代内存的大小 -XX:NewSize=1024m -XX:MaxNewSize=256m
MaxNewSize = 1073741824 (1024.0MB)
OldSize = 5439488 (5.1875MB)#老生代的内存大小
NewRatio = 2
SurvivorRatio = 2
PermSize = 167772160 (160.0MB)#持久生代 永生代 -XX:PermSize=160m -XX:MaxPermSize=160m
MaxPermSize = 167772160 (160.0MB)
Heap Usage:
PS Young Generation
Eden Space:
capacity = 1045364736 (996.9375MB) #NewSize= S0 + S1 + E
used = 289950536 (276.5183792114258MB)
free = 755414200 (720.4191207885742MB)
27.7367818154524% used
From Space: #S0 S1
capacity = 14548992 (13.875MB)
used = 6190208 (5.9034423828125MB)
free = 8358784 (7.9715576171875MB)
42.54733248873874% used
To Space:
capacity = 13828096 (13.1875MB)
used = 0 (0.0MB)
free = 13828096 (13.1875MB)
0.0% used
PS Old Generation
capacity = 4294967296 (4096.0MB) #老生代的内存大小 = total heap 大小 - Newsize 大小
used = 369568008 (352.44751739501953MB)
free = 3925399288 (3743.5524826049805MB)
8.604675717651844% used
PS Perm Generation
capacity = 167772160 (160.0MB) #永生代大小
used = 105796384 (100.89529418945312MB)
free = 61975776 (59.104705810546875MB)
63.0595588684082% used
用jstack dump 线程的信息,看当时线程有没有问题;
进程查找所有的线程:top -H -p 进程pid会打印出此进程所有的线程;然后在dump 线程的栈信息
sudo -u admin jstack 6245 > stack.log
#"DubboClientHandler-172.22.6.25:20880-thread-155"
#daemon prio=10 tid=0x00000000576de800 nid=0x687a waiting on condition [0x000000
0054f81000]
gclog的分析:
#前提是在JVM 配置中加入Xloggc的配置:
-XX:+UseParallelGC -Xloggc:/home/.../.../gc.log
cat gc.log
#java进程启动到现在的时间(s) GC的类别 GC的减少内存的结果 这次GC 花的时间
62802.998: [GC 3152988K->2129998K(5231808K), 0.0241120 secs]
62846.660: [GC 3156430K->2133046K(5232896K), 0.0223710 secs]
62877.147: [GC 3161142K->2134765K(5232384K), 0.0197390 secs]
#下面这行的含义:
#62802.998: [GC 3152988K->2129998K(5231808K), 0.0241120 secs]
#62802.998: #这次gc 从java进程启动到现在执行的时间,单位是秒
#GC:GC的种类,代表是YGC 如果Full GC 说明是FGC
#3152988K:这次GC 之前整个堆内存(Heap 占用大小)
#2129998K:这次GC 之后整个堆内存(Heap 占用大小)
#也就是这次GC 的效果是释放了(3152988K - 2129998K)内存
#(5231808K) :代表整个JVM 最大的堆内存 Xmx
#0.0241120 secs: 此次GC 耗费的时间;如果Full GC 则在这个耗费时间里面java 服务暂停服务
4. JVM 名词解释
- .程序计数器
每一个Java线程都有一个程序计数器来用于保存程序执行到当前方法的哪一个指令。
- 线程栈
线程的每个方法被执行的时候,都会同时创建一个帧(Frame)用于存储本地变量表、操作栈、动态链接、方法出入口等信息。每一个方法的调用至完成,就意味着一个帧在VM栈中的入栈至出栈的过程。如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常;如果VM栈可以动态扩展(VM Spec中允许固定长度的VM栈),当扩展时无法申请到足够内存则抛出OutOfMemoryError异常。
- 堆
每个线程的栈都是该线程私有的,堆则是所有线程共享的。当我们new一个对象时,该对象就被分配到了堆中。但是堆,并不是一个简单的概念,堆区又划分了很多区域,为什么堆划分成这么多区域,这是为了JVM的内存垃圾收集,似乎越扯越远了,扯到垃圾收集了,现在的jvm的gc都是按代收集,堆区大致被分为三大块:新生代,旧生代,持久代(虚拟的);新生代又分为eden区,s0区,s1区。新建一个对象时,基本小的对象,生命周期短的对象都会放在新生代的eden区中,eden区满时,有一个小范围的gc(minor gc),整个新生代满时,会有一个大范围的gc(major gc),将新生代里的部分对象转到旧生代里。
- 方法区
其实就是永久代(Permanent Generation),方法区中存放了每个Class的结构信息,包括常量池、字段描述、方法描述等等。VM Space描述中对这个区域的限制非常宽松,除了和Java堆一样不需要连续的内存,也可以选择固定大小或者可扩展外,甚至可以选择不实现垃圾收集。相对来说,垃圾收集行为在这个区域是相对比较少发生的,但并不是某些描述那样永久代不会发生GC(至 少对当前主流的商业JVM实现来说是如此),这里的GC主要是对常量池的回收和对类的卸载,虽然回收的“成绩”一般也比较差强人意,尤其是类卸载,条件相当苛刻。
- 常量池
Class文件中除了有类的版本、字段、方法、接口等描述等信息外,还有一项信息是常量表(constant_pool table),用于存放编译期已可知的常量,这部分内容将在类加载后进入方法区(永久代)存放。但是Java语言并不要求常量一定只有编译期预置入Class的常量表的内容才能进入方法区常量池,运行期间也可将新内容放入常量池(最典型的String.intern()方法)。
回收机制的选择
http://hxraid.iteye.com/blog/746064
http://chenchendefeng.iteye.com/blog/455883