`

JVM内存模型以及垃圾收集策略解析

    博客分类:
  • jvm
 
阅读更多

今天接着补全上篇博文。

三 垃圾收集策略配置

3.1 吞吐量优先

吞吐量是指GC的时间与运行总时间的比值,比如系统运行了100分钟,而GC占用了一分钟,那么吞吐量就是99%,吞吐量优先一般运用于对响应性要求不高的场合,比如web应用,因为网络传输本来就有延迟的问题,GC造成的短暂的暂停使得用户以为是网络阻塞所致。

吞吐量优先可以通过-XX:GCTimeRatio来指定。

当通过-XX:GCTimeRatio不能满足系统的要求以后,我们可以更加细致的来对JVM进行调优。

首先因为要求高吞吐量,这样就需要一个较大的Young generation,此时就需要引入“Parallel scavenging Collector”,可以通过参数:-XX:UseParallelGC来配置。

 java -server -Xms3072m -Xmx3072m -XX:NewSize=2560m -XX:MaxNewSize=2560 XX:SurvivorRatio=2 -XX:+UseParallelGC 

当年轻代使用了"Parallel scavenge collector"后,老生代就不能使用"CMS"GC了,在JDK1.6之前,此时老生代只能采用串行收集,而JDK1.6引入了并行版本的老生代收集器,可以用参数-XX:UseParallelOldGC来配置

3.1.1 控制并行的线程数

缺省情况下,Parallel scavenging Collector 会开启与cpu数量相同的线程进行并行的收集,但是也可以调节并行的线程数。假如你想用4个并行的线程去收集Young generation的话,那么就可以配置-XX:ParallelGCThreads=4,此时JVM的配置参数如下:

 java -server -Xms3072m -Xmx3072m -XX:NewSize=2560m -XX:MaxNewSize=2560 XX:SurvivorRatio=2 -XX:+UseParallelGC -XX:ParallelGCThreads=4

3.1.2 自动调节新生代

在采用了"Parallel scavenge collector"后,此GC会根据运行时的情况自动调节survivor ratio来使得性能最优,因此"Parallel scavenge collector"应该总是开启此参数。

此时JVM的参数配置如下:

java -server -Xms3072m -Xmx3072m -XX:+UseParallelGC    -XX:ParallelGCThreads=4 -XX:+UseAdaptiveSizePolicy

 

3.2 响应时间优先

响应时间优先是指GC每次运行的时间不能太久,这种情况一般使用与对及时性要求很高的系统,比如股票系统等。

响应时间优先可以通过参数-XX:MaxGCPauseMillis来配置,配置以后JVM将会自动调节年轻代,老生代的内存分配来满足参数设置。

在一般情况下,JVM的默认配置就可以满足要求,只有默认配置不能满足系统的要求时候,才会根据具体的情况来对JVM进行性能调优。如果采用默认的配置不能满足系统的要求,那么此时就可以自己动手来调节。

此时"Young generation"可以采用"Parallel copying collector",而"Old generation"则可以采用"Concurrent Collector",

举个例子来说,以下参数设置了新生代用Parallel Copying Collector老生代采用CMS收集器

java -server -Xms512m -Xmx512m  -XX:NewSize=64m -XX:MaxNewSize=64m -XX:SurvivorRatio=2         -XX:+UseConcMarkSweepGC -XX:+UseParNewGC 

此时需要注意两个问题:

如果没有指定-XX:+UseParNewGC,则采用默认的非并行版本的copy collector.

2  如果在一个单CPU的系统上设置了-XX:+UseParNewGC ,则默认还是采用缺省的copy collector.

3.2.1 控制并行的线程数

默认情况下,Parallel copy collector启动和CPU数量一样的线程,也可以通过参数-XX:ParallelGCThreads来指定,比如你想用3个线程去进行并发的复制收集,那么可以改变上述参数如下:

java -server -Xms512m -Xmx512m -XX:NewSize=64m  -XX:MaxNewSize=64m -XX:SurvivorRatio=2        -XX:ParallelGCThreads=4    -XX:+UseConcMarkSweepGC       -XX:+UseParNewGC 

3.2.2 控制并发收集的临界值

默认情况下,CMS gc"old generation"空间占用率高于68%的时候,就会进行垃圾收集,而如果想控制收集的临界值,可以通过参数:-XX:CMSInitiatingOccupancyFraction来控制,比如改变上述的JVM配置如下:

java -server -Xms512m -Xmx512m -XX:NewSize=64m  -XX:MaxNewSize=64m -XX:SurvivorRatio=2        -XX:ParallelGCThreads=4    -XX:+UseConcMarkSweepGC       -XX:+UseParNewGC      -XX:CMSInitiatingOccupancyFraction=35

 

四 GC触发以及常见的内存错误

4.1 Minor GC的触发

Minor GC主要负责收集Young GenerationMinor GC一般在新生代不够用的情况下触发,比如我们一次性创建了很多对象等。

List<byte[]> buffer = new ArrayList<byte[]>();for(int i=0;i<8*1024;i++){

buffer.add(new byte[1024]);

}

以上代码通过一个字节数组的List模拟触发Minor gc,设置JVM参数如下:

-verbose:gc -Xmn10M -Xms64M -Xmx64M -XX:+PrintGC

设置以上参数以后,因为-Xmn=10M,默认-XX:SurvivorRatio=8 ,则eden的空间大小为8M,当eden对象大小超过8M的时候就会触发Minor gc.

运行的结果如下:

[GC 8192K->8030K(64512K), 0.0243391 secs]

从运行结果可以看出,gc前和gc后的eden区的占用情况,需要注意的是括号里(64512)这个数值时63M,它不包括一块Survivor 空间。

这里需要注意的一点就是,如果创建的对象大于eden的大小,那么将不会通过Survivor空间复制,直接转移到old generation.

调整以上代码如下:

List<byte[]> buffer = new ArrayList<byte[]>();

buffer.add(new byte[8*1024*1024]);

通过同样的JVM参数运行,则发现不会触发Minor gc,这是因为对象超过了eden的大小,从而直接分配到了Old generation.

 

4.2 Major GC的触发

4.2.1 Old Generation空间满或者接近某一个比例

Old generation 空间满是因为Young generation提升到Old generation的对象+Old generation的本来的大小已经接近或者超过了Old generation的大小。对于CMS GC,当Old generation空间使用率接近某一个比例,可以通过参数-XX:CMS InitialingOccupancyFraction,此参数表示Old generation的使用率,默认为68%

Young generation对象提升到Old generation对象有以下三种情况:

Ø 分配的对象大于eden空间的大小

Ø Young generation代中经过了-XX:MaxTenuringThreshold次复制任然存活的对象

Ø Minor gc的时候,放不进to survivor的对象

Major GC以后,如果还没有足够的空间可以用的话,此时就会抛出java.lang.OutOfMemoryjava heap space,当出现此错误的时候,说明可能存在内存泄露现象的,这时候就需要我们对程序进行检查看看什么地方存在内存泄露的。

我们可以通过以下代码来模拟一下java.lang.OutOfMemory:java heap space的发生:

List<byte[]> buffer = new ArrayList<byte[]>();

buffer.add(new byte[10*1024*1024]);

以上代码分配了一个10M的字节数组,我们通过以下的参数运行:

-verbose:gc -Xmn10M -Xms20M -Xmx20M -XX:+PrintGC

以上参数指定Young generation的空间大小为10MOld generation空间大小为10M

运行结果如下:

[GC 327K->134K(19456K), 0.0056516 secs]

[Full GC 134K->134K(19456K), 0.0178891 secs]

[Full GC 134K->131K(19456K), 0.0141412 secs]

Exception in thread "main" java.lang.OutOfMemoryError:   Java heap space

        at Test.main(Test.java:30)

从运行结果可以看出,JVM进行了一次Minor gc和两次的Major gc,从Major gc的输出可以看出,gc以后old区使用率为134K,而字节数组为10M,加起来大于了old generation的空间,所以抛出了异常,如果调整-Xms21M,-Xmx21M,那么就不会触发gc操作也不会出现异常了。

4.2.2 Perm Generation 空间满

Perm Generation空间主要存放Class对象,Field,Method对象,当一次性加载太多的类或者在热部署以后不卸载类的情况(比如在Jboss服务器中,如果经常热部署一些应用就会出现Perm 空间溢出)就会造成Perm Generation被占满,此时就会出现:

java.lang.OutOfMemory:PermGen space,在出现此异常的时候,如果是因为热部署引起的,我们重新启动AS就可以了,如果是因为加载的类太多,此时可以通过-XX:PermSize-XX:MaxPermSize调整。

4.3 常见内存错误分析

4.3.1 StackOverflowError 

java.lang.StackOverflowError错误表示JVM栈溢出,出现这个错误的原因一般都是递归的层次太深,或者无限的递归造成的。出现这种错误的时候首先要对应用程序进行检查,看看是那些代码造成了栈溢出,如果是递归造成的可以改为迭代方式实现。

JVM同样也提供了一个参数来让我们调节运行时栈空间的大小。-XX:Xss=256K表示栈空间最大为256K.我们也可以调大,但是建议不要对此参数进行调节。

4.3.2 OutOfMemoryError: Java heap space.

java.lang.OutOfMemoryError: Java heap space这个错误表示JVM的新生代和老生代的内存不足。出现这个错误说明应用程序出现了内存溢出或者程序所需要的内存大于JVM的内存设置了。

遇到这个问题的时候,首先我们可以调节JVMHeap内存的大小,具体可以通过-Xmx -Xms来进行设置,如果设置大以后还是会出现内存溢出,那么说明应用程序本身存在内存泄露,这个时候就需要我们对应用程序进行检查,找出导致内存泄露的地方,然后修正。

4.3.3 OutOfMemory:PermGen space

java.lang.OutOfMemory:PermGen space错误是由Perm space空间不足。一般出现这个错误是由加载了太多的类或者大量使用了动态代理造成的。如果出现了这个错误,我们可以将Perm空间调大一点。

-XX:PermSize=16M  -XX:MaxPermSize=64M

 

参考资料

http://developers.sun.com/mobility/midp/articles/garbage/

http://developers.sun.com/mobility/midp/articles/garbagecollection2/

http://blogs.sun.com/watt/resource/jvm-options-list.html

http://java.sun.com/developer/technicalArticles/Programming/turbo/

http://www.ibm.com/developerworks/library/j-jtp10283/index.html?S_TACT=105AGX52&S_CMP=cn-a-j

http://www.ibm.com/developerworks/library/j-jtp11253/index.html?S_TACT=105AGX52&S_CMP=cn-a-j

分享到:
评论

相关推荐

    JVM内存模型及垃圾收集策略解析

    NULL 博文链接:https://forrest420.iteye.com/blog/1127427

    深入理解JVM内存结构及运行原理全套视频加资料.txt

    2019最新深入理解JVM内存结构及运行原理(JVM调优)高级核心课程视频教程下载。JVM是Java知识体系中的重要部分,对JVM底层的了解是每一位Java程序员深入Java技术领域的重要因素。本课程试图通过简单易懂的方式,系统...

    Java面试通关宝典:深度解读核心知识点与实战技巧,全面提升面试表现力与技术实力

    JVM与性能优化:这部分问题涵盖了JVM内存模型、垃圾收集、性能调优等内容。例如,解释JVM的内存区域划分和作用;理解垃圾收集算法和调优策略;讨论如何分析和优化Java应用程序的性能等。 通过深入学习和理解这些问题...

    zxing.java源码解析-JavaAndroidInterview:Android、JavaSE、数据结构与算法豆知识,可用于碎片化学习和

    zxing.java源码解析 这是一个JavaSE、Android领域的豆知识tips,可以用来...JVM内存模型和垃圾收集 3. 垃圾收集策略 4. G1收集器 5. Java引用类型 网络传输 正则表达式 Git 计算机组成原理(正在填补中……) 言职 附录:

    深入理解_Java_虚拟机 JVM_高级特性与最佳实践

    112 5.2.5 服务器JVM进程崩溃 / 113 5.3 实战:Eclipse运行速度调优 / 114 5.3.1 调优前的程序运行状态 / 114 5.3.2 升级JDK 1.6的性能变化及兼容问题 / 117 5.3.3 编译时间和类加载时间的优化 / 122 5.3.4 ...

    Java虚拟机

    第五部分探讨了Java实现高效并发的原理,包括JVM内存模型的结构和操作;原子性、可见性和有序性在Java内存模型中的体现;先行发生原则的规则和使用;线程在Java语言中的实现原理;虚拟机实现高效并发所做的一系列锁...

    Hadoop实战(第2版)

    9.3.4 其他Mahout clustering 算法 .9.4 本章小结第5 部分 驯服大象10 深入解析 Hive10.1 Hive 基础10.1.1 安装10.1.2 元存储10.1.3 数据库、表、分区和存储10.1.4 数据模型10.1.5 查询...

    Hadoop硬实战 [(美)霍姆斯著][电子工业出版社][2015.01]_PDF电子书下载 带书签目录 高清完整版.rar )

    4.1.4 为你的数据挑选最优的合并策略 4.2 排序 4.2.1 二次排序 技术点21 二次排序的实现 4.2.2 整体并行排序 技术点22 通过多个reducer 对key 进行排序 4.3 抽样 技术点23 蓄水池抽样(reservoir ...

    java 面试题 总结

     GC是垃圾收集的意思(Gabage Collection),内存处理是编程人员容易出现问题的地方,忘记或者错误的内存回收会导致程序或系统的不稳定甚至崩溃,Java提供的GC功能可以自动监测对象是否超过作用域从而达到自动回收...

    超级有影响力霸气的Java面试题大全文档

     GC是垃圾收集的意思(Gabage Collection),内存处理是编程人员容易出现问题的地方,忘记或者错误的内存回收会导致程序或系统的不稳定甚至崩溃,Java提供的GC功能可以自动监测对象是否超过作用域从而达到自动回收...

    java核心知识点整理.pdf

    JVM 内存区域 ..................................................................................................................................... 21 2.2.1. 程序计数器(线程私有) ........................

    JAVA核心知识点整理(有效)

    2.2. JVM 内存区域 ..................................................................................................................................... 21 2.2.1. 程序计数器(线程私有) ....................

Global site tag (gtag.js) - Google Analytics