标记--清除算法 缺点 造成内存碎片
复制算法 空间利用不够 任何时候都只使用一般空间
标记整理算法 成本高,但是解决了内存碎片的缺点
Serial收集器 是虚拟机新生代收集器的唯一选择
优点:简单而高效,对于限制单个CPU来说,由于没有线程交互的开销,专心做垃圾收集自然可以获得最高的单线程收集效率
ParNew收集器 是单线程收集器的多线程版本
ParNew收集器是许多运行在Server模式下的虚拟机中首选的新生代收集器。
有一个很重要的原因是除了Serial收集器外,目前只有它能与CMS收集器配合工作
Parallel收集器 吞吐量优先的收集器 是一个新生代收集器 他也是使用复制算法的收集器,又是并行的多线程收集器
优点:停顿时间越短就越适合需要与用户交互的程序,良好的响应速度能提升用户体验, 在注重吞吐量以及CPU资源敏感的场合,都可以优先考虑Parallel Scavenge加Parallel Old收集器。
而高吞吐量则可以高效率地利用CPU时间,尽快完成程序的运算任务,主要适合在后台运算而不需要太多交互的任务。
CMS收集器 是一种以获取最短回收停顿时间为目标的收集器,,这类回收器尤其注重服务的相应速度,希望系统停顿最短,给用户达到最好的体验,
cms 收集器是基于标记-清除算法实现的
G1收集器 是一款面向服务端应用的收集器,
特点:并行与并发 ,分代收集,空间整合,可预测的停顿,
/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
java -Xmx3550m -Xms3550m -Xmn2g -Xss128k
-Xms2550m 初始化堆内存
-Xmx3350m 最大堆内存 一般初始化堆内存和最大内存一致,以避免每次垃圾回收完成之后JVM重新分配内存
-Xmn2g 设置年轻代大小为2G 整个堆大小=年轻代+老年代+持久代 持久代一般固定为64m,一般按照3/8 比例进行分配
-Xss128 设置每个线程的堆栈大小
java -Xmx3550m -Xms3550m -Xss128k -XX:NewRatio=4 -XX:SurvivorRatio=4 -XX:MaxPermSize=16m -XX:MaxTenuringThreshold=0
-XX:NewRatio=4 设置年轻代(包括eden区和servivor区)于年老代的比例,表示年轻代于年老代所占比值为1/4 ,年轻代占整个堆的1/5
-XX:SurvivorRatio=4 设置年轻代中Eden区与Survivor区的大小比值。设置为4,则两个Survivor区与一个Eden区的比值为2:4,一个Survivor区占整个年轻代的1/6
-XX:MaxPermSize=16m:设置持久代大小为16m。
-XX:MaxTenuringThreshold=0:设置垃圾最大年龄。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代。对于年老代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活时间,增加在年轻代即被回收的概论
吞吐量优先的并行收集器
java -Xmx3800m -Xms3800m -Xmn2g -Xss128k -XX:+UseParallelGC
-XX:ParallelGCThreads=20
主要讲两个:
-XX:+UseParallelGC 垃圾收集器为并行收集器 ,此配置对年轻代有效,上述配置:年轻代使用并发收集,年老代用串行收集
-XX:ParallelGCThreads=20 配置并行收集器的线程数,即:同时多少个线程一起进行垃圾回收,此值最好配置于于处理器数目相等
Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20
-XX:+UseParallelOldGC
-XX:+UseParallelOldGC: 配置老年代垃圾收集方式为并行收集,jdk6 支持对老年代进行收集
java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC
-XX:MaxGCPauseMillis=100
-XX:MaxGCPauseMillis=100 设置每次年轻代垃圾回收的最长时间,如果没法满足此时间, JVM会自动调节年轻代大小,以满足为止
java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC
-XX:MaxGCPauseMillis=100-XX:+UseAdaptiveSizePolicy
-XX:+UseAdapativeSizePolicy:并行收集器会自动选择年轻代区大小和相应的survivor区的比例,以达到目标系统规定的最低相应时间或者收集频率,此值建议使用并行收集器时,一直打开
响应时间优先的收集器
java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:ParallelGCThreads=20
-XX:+UseConcMarkSweepGC -XX:+UseParNewGC
-XX:+UseConcMarkSweepGC:设置老年代为并发收集,配置这个之后,-XX:NewRatio=4的配置就失效了,原因不明,所以此时年轻代大小最好用-Xmn设置
-XX:+UseParNewGC :年轻代为并行收集,可与CMS收集器同时使用,jdk1.5以上JVM会更具系统配置自动设置,所以无需设置此值
java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseConcMarkSweepGC
-XX:CMSFullGCsBeforeCompaction=5 -XX:+UseCMSCompactAtFullCollection
-XX:CMSFullGCBeforeCompaction=5:由于并发收集器不对内存空间进行压缩、整理,所以运行一段时间后会产生“碎片”,使的运行效率降低,此值设置运行多少次GC以后对内存空间进行压缩,整理
-XX:+UseCMSCompactAtFullCollectio:打开对老年代的压缩,可能会影响性能,但是可以消除碎片
-XX:+PrintGC
输出形式:[GC 118250K->113543K(130112K), 0.0094143 secs]
[Full GC 121376K->10414K(130112K), 0.0650971 secs]
-XX:+PrintGCDetails
输出形式:[GC [DefNew: 8614K->781K(9088K), 0.0123035 secs] 118250K->113543K(130112K), 0.0124633 secs]
[GC [DefNew: 8614K->8614K(9088K), 0.0000665 secs][Tenured: 112761K->10414K(121024K), 0.0433488 secs] 121376K->10414K(130112K), 0.0436268 secs]
-XX:+PrintGCTimeStamps -XX:+PrintGC:PrintGCTimeStamps可与上面两个混合使用
输出形式:11.851: [GC 98328K->93620K(130112K), 0.0082960 secs]
-XX:+PrintGCApplicationConcurrentTime:打印每次垃圾回收前,程序未中断的执行时间。可与上面混合使用
输出形式:Application time: 0.5291524 seconds
-XX:+PrintGCApplicationStoppedTime:打印垃圾回收期间程序暂停的时间。可与上面混合使用
输出形式:Total time for which application threads were stopped: 0.0468229 seconds
-XX:PrintHeapAtGC:打印GC前后的详细堆栈信息
-Xloggc:filename:与上面几个配合使用,把相关日志信息记录到文件以便分析。
参数名称
含义
默认值
说明
-XX:+UseParallelGC
Full GC采用parallel MSC(此项待验证)
选择垃圾收集器为并行收集器.此配置仅对年轻代有效.即上述配置下,年轻代使用并发收集,而年老代仍旧使用串行收集.(此项待验证)
-XX:+UseParNewGC
设置年轻代为并行收集
可与CMS收集同时使用,JDK5.0以上,JVM会根据系统配置自行设置,所以无需再设置此值
-XX:ParallelGCThreads
并行收集器的线程数
此值最好配置与处理器数目相等 同样适用于CMS
-XX:+UseParallelOldGC
年老代垃圾收集方式为并行收集(Parallel Compacting)
这个是JAVA 6出现的参数选项
-XX:MaxGCPauseMillis
每次年轻代垃圾回收的最长时间(最大暂停时间)
如果无法满足此时间,JVM会自动调整年轻代大小,以满足此值.
-XX:+UseAdaptiveSizePolicy
自动选择年轻代区大小和相应的Survivor区比例
设置此选项后,并行收集器会自动选择年轻代区大小和相应的Survivor区比例,以达到目标系统规定的最低相应时间或者收集频率等,此值建议使用并行收集器时,一直打开.
-XX:GCTimeRatio
设置垃圾回收时间占程序运行时间的百分比
公式为1/(1+n)
-XX:+ScavengeBeforeFullGC
Full GC前调用YGC
true
Do young generation GC prior to a full GC. (Introduced in 1.4.1.)
参数名称
含义
默认值
说明
-XX:+UseConcMarkSweepGC
使用CMS内存收集
测试中配置这个以后,-XX:NewRatio=4的配置失效了,原因不明.所以,此时年轻代大小最好用-Xmn设置
-XX:+AggressiveHeap
试图是使用大量的物理内存长时间大内存。使用的优化,能检查计算资源(内存, 处理器数量)至少需要256MB内存,大量的CPU/内存, (在1.4.1在4CPU的机器上已经显示有提升)
-XX:CMSFullGCsBeforeCompaction
多少次后进行内存压缩
由于并发收集器不对内存空间进行压缩,整理,所以运行一段时间以后会产生"碎片",使得运行效率降低.此值设置运行多少次GC以后对内存空间进行压缩,整理.
-XX:+CMSParallelRemarkEnabled
降低标记停顿
-XX+UseCMSCompactAtFullCollection
在FULL GC的时候, 对年老代的压缩
CMS是不会移动内存的, 因此, 这个非常容易产生碎片, 导致内存不够用, 因此, 内存的压缩这个时候就会被启用。 增加这个参数是个好习惯。可能会影响性能,但是可以消除碎片
-XX:+UseCMSInitiatingOccupancyOnly
使用手动定义初始化定义开始CMS收集
禁止hostspot自行触发CMS GC
-XX:CMSInitiatingOccupancyFraction=70
使用cms作为垃圾回收,使用70%后开始CMS收集
92
为了保证不出现promotion failed(见下面介绍)错误,该值的设置需要满足以下公式CMSInitiatingOccupancyFraction计算公式
-XX:CMSInitiatingPermOccupancyFraction
设置Perm Gen使用到达多少比率时触发
92
-XX:+CMSIncrementalMode
设置为增量模式
用于单CPU情况
-XX:+CMSClassUnloadingEnabled
相对于并行收集器,CMS收集器默认不会对永久代进行垃圾回收。如果希望对永久代进行垃圾回收,可用设置标志-XX:+CMSClassUnloadingEnabled。在早期JVM版本中,要求设置额外的标志-XX:+CMSPermGenSweepingEnabled。注意,即使没有设置这个标志,一旦永久代耗尽空间也会尝试进行垃圾回收,但是收集不会是并行的,而再一次进行Full GC。
1系统崩溃的一些现象:
1、每次垃圾回收的时间越来越长,由之前的10ms延长到50ms,Full GC 的时间也由原来的0.5s延长到4、5s
2、Full GC 的次数越来越多,最频繁时隔不到1分钟就进行Full GC
3、年老代的内存越来越大,并且Full GC 后年老代内存没有释放
2生成堆的dump文件
通过JMX的MBean生成当前的Heap信息,大小为一个3G(整个堆的大小)的hprof文件,如果没有启动JMX可以通过Java的jmap命令来生成该文件。
3分析dump文件
下面要考虑的是如何打开这个3G的堆信息文件,显然一般的Window系统没有这么大的内存,必须借助高配置的Linux。当然我们可以借助X-Window把Linux上的图形导入到Window。
我们考虑用下面几种工具打开该文件:
Visual VMIBM HeapAnalyzerJDK 自带的Hprof工具使用这些工具时为了确保加载速度,建议设置最大内存为6G。使用后发现,这些工具都无法直观地观察到内存泄漏,Visual VM虽能观察到对象大小,但看不到调用堆栈;HeapAnalyzer虽然能看到调用堆栈,却无法正确打开一个3G的文件。因此,我们又选用了Eclipse专门的静态内存分析工具:Mat
4分析内存泄漏
通过Mat我们能清楚地看到,哪些对象被怀疑为内存泄漏,哪些对象占的空间最大及对象的调用关系。针对本案,在ThreadLocal中有很多的JbpmContext实例,经过调查是JBPM的Context没有关闭所致。
另,通过Mat或JMX我们还可以分析线程状态,可以观察到线程被阻塞在哪个对象上,从而判断系统的瓶颈。
5回归问题
Q:为什么崩溃前垃圾回收的时间越来越长?
A:根据内存模型和垃圾回收算法,垃圾回收分两部分:内存标记、清除(复制),标记部分只要内存大小固定时间是不变的,变的是复制部分,因为每次垃圾回收都有一些回收不掉的内存,所以增加了复制量,导致时间延长。所以,垃圾回收的时间也可以作为判断内存泄漏的依据
Q:为什么Full GC的次数越来越多?
A:因此内存的积累,逐渐耗尽了年老代的内存,导致新对象分配没有更多的空间,从而导致频繁的垃圾回收
Q:为什么年老代占用的内存越来越大?
A:因为年轻代的内存无法被回收,越来越多地被Copy到年老代
年老代堆空间被占满
这种方式解决起来也比较容易,一般就是根据垃圾回收前后情况对比,同时根据对象引用情况(常见的集合对象引用)分析,基本都可以找到泄漏点。
持久代被占满
Perm空间被占满。无法为新的class分配存储空间而引发的异常。这个异常以前是没有的,但是在Java反射大量使用的今天这个异常比较常见了。主要原因就是大量动态反射生成的类不断被加载,最终导致Perm区被占满。
更可怕的是,不同的classLoader即便使用了相同的类,但是都会对其进行加载,相当于同一个东西,如果有N个classLoader那么他将会被加载N次。因此,某些情况下,这个问题基本视为无解。当然,存在大量classLoader和大量反射类的情况其实也不多。
解决:
1.-XX:MaxPermSize=16m2.换用JDK。比如JRocket堆栈溢出
异常:java.lang.StackOverflowError说明:这个就不多说了,一般就是递归没返回,或者循环调用造成线程堆栈满了
异常:Fatal: Stack size too small
说明:java中一个线程的空间大小是有限制的。JDK5.0以后这个值是1M。与这个线程相关的数据将会保存在其中。但是当线程空间满了以后,将会出现上面异常。
解决:增加线程栈大小。-Xss2m。但这个配置无法解决根本问题,还要看代码部分是否有造成泄漏的部分。
系统内存被占满
异常:java.lang.OutOfMemoryError: unable to create new native thread
说明:
这个异常是由于操作系统没有足够的资源来产生这个线程造成的。系统创建线程时,除了要在Java堆中分配内存外,操作系统本身也需要分配资源来创建线程。因此,当线程数量大到一定程度以后,堆中或许还有空间,但是操作系统分配不出资源来了,就出现这个异常了。
分配给Java虚拟机的内存愈多,系统剩余的资源就越少,因此,当系统内存固定时,分配给Java虚拟机的内存越多,那么,系统总共能够产生的线程也就越少,两者成反比的关系。同时,可以通过修改-Xss来减少分配给单个线程的空间,也可以增加系统总共内生产的线程数。
解决:
1.重新设计系统减少线程数量。2.线程数量不能减少的情况下,通过-Xss减小单个线程大小。以便能生产更多的线程。