`

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

 

参考资料

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

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

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

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

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

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

 

10
1
分享到:
评论
3 楼 panhf2003 2010-07-14  
61234865 写道
写得非常好,不过目前就国内的情况来看,都是有更高的硬件来解决这些性能问题,只有极少数的软件公司 会要求去调整JVM来提高性能。当然,最简单的设置还是有的,只是不会这么深入

这就不对了,如果内存溢出或者一些系统缺陷导致的问题,硬件配置高了也照样会出问题,只是这个问题的发现时间会推迟罢了,根本问题还是没解决
2 楼 xvm03 2010-05-28  
写的非常好,受益良多,就是这排版,嘿嘿
1 楼 61234865 2010-03-03  
写得非常好,不过目前就国内的情况来看,都是有更高的硬件来解决这些性能问题,只有极少数的软件公司 会要求去调整JVM来提高性能。当然,最简单的设置还是有的,只是不会这么深入

相关推荐

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

    总的来说,理解和掌握JVM内存模型及垃圾收集策略对于优化Java应用程序的性能至关重要。开发者需要根据应用特点调整内存参数,选择合适的垃圾收集策略,以避免内存溢出、提高系统效率并确保程序的稳定运行。

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

    **JVM内存模型及垃圾收集策略解析** Java虚拟机(JVM)是Java程序运行的基础,它为Java程序提供了一个跨平台的运行环境。在深入理解JVM内存模型与垃圾收集策略之前,我们首先需要知道JVM的主要组成部分:类装载器、...

    jvm内存模型以及垃圾回收机制.pptx

    Java虚拟机(JVM)内存模型...理解JVM内存模型和垃圾回收机制对于优化Java应用性能、避免内存泄漏和有效利用资源至关重要。开发者应根据实际需求选择合适的垃圾回收器,并关注内存分配策略,以实现高效稳定的程序运行。

    JVM内存模型

    HotSpot VM在Java 5中引入了一系列自适应调优特性,能够在JVM启动时根据底层平台和系统配置自动选择合适的垃圾收集器、配置Java堆大小以及选择运行时JIT编译器。例如,在Server模式下,初始堆或堆的最小值(`-Xms`)...

    JVM内存模型(通俗易懂)

    JVM内存模型则是理解Java程序性能优化的关键,因为它规定了程序中的数据如何在内存中分配和访问。本文将深入浅出地探讨JVM内存模型,帮助你快速掌握这一核心概念。 首先,我们要知道JVM内存模型主要分为以下几个...

    jvm相关代码仓库,包括类加载机制,字节码执行机制,JVM内存模型,GC垃圾回收,JV-jvm_practice.zip

    3. **JVM内存模型**: JVM内存主要分为堆内存(Heap)、栈内存(Stack)、方法区(Method Area)、程序计数器(PC Register)和本地方法栈(Native Method Stack)。重点理解对象创建、内存分配以及栈帧的工作方式...

    java -jvm 内存分配和jvm调优

    Java JVM(Java虚拟机)内存分配与调优是Java...通过理解JVM内存模型,选择合适的垃圾收集器和设置合理的内存参数,可以有效提升Java应用的性能和稳定性。在实践中,不断学习和试验,才能找到最适合应用的内存配置。

    java中jvm内存分配相关资料总结整理

    垃圾收集器会定期检查不再使用的对象并回收其占用的空间。 3. **垃圾回收** - **GC(Garbage Collection)**:Java的自动内存管理机制,负责清理无用对象,避免内存泄漏。 - **GC算法**:常见的有标记-清除、复制...

    介绍JVM和Java内存模型的两篇非常不错的资料

    了解JVM内存模型后,我们来看看JVM的工作原理。当Java程序启动时,JVM会进行加载、验证、准备、解析和初始化五个阶段: 1. **加载**:JVM找到并读取.class文件,将其转换为内部数据结构。 2. **验证**:确保字节码...

    JVM深入解析(JVM specification 和Sun的JVM的内存机制)

    2. JVM内存模型 - JVM规范定义了不同的内存区域,包括程序计数器、Java虚拟机栈、本地方法栈、堆和方法区(在Java 8之后变为元空间)。 - Java虚拟机栈:每个线程都有一个独立的栈,用于存储方法调用的状态,包括...

    JVM常见面试题解析.pdf

    2. JVM内存模型:JVM内存分为五个区域:程序计数器、虚拟机栈、本地方法栈、Java堆和方法区。程序计数器记录当前线程执行的字节码指令地址;虚拟机栈和本地方法栈分别服务于Java方法和本地(Native)方法;Java堆是...

    JVM内存资料.zip_jdk

    《JVM内存模型深度探索——基于JDK源码解析》 在Java开发中,深入理解JVM(Java Virtual Machine)内存模型是至关重要的。本文将从JDK源码的角度出发,详细探讨JVM的内存结构、类加载机制以及对象创建与内存分配...

    深入浅出jvm虚拟机视频大全(jvm性能调优+内存模型+虚拟机原理)

    ### JVM内存模型详解 #### 1. 方法区 方法区主要存储类的信息、常量、静态变量等数据。这些数据通常在类加载时创建,并且在整个JVM生命周期中保持不变。对于HotSpot虚拟机而言,这部分区域被称为“永久代”(Perm...

    jdk1.8的jvm内存模型——实习生必须了解的

    【JDK 1.8 的 JVM 内存模型】 JVM(Java Virtual ...了解JVM内存模型和垃圾收集算法是Java开发者的基本功,对于实习生来说,掌握这些知识不仅能帮助应对面试,也能在实际开发中更好地理解和优化程序的内存使用。

    深入理解JVM&G1; GC

    总之,《深入理解JVM & G1 GC》这本书为读者提供了理解JVM内存模型和G1 GC的深入见解,有助于Java开发者更好地理解和控制JVM的内存管理,提升应用程序的稳定性和效率。通过学习这些知识,开发者可以解决实际开发中...

    JVM入门到JVM 调优实战

    总结来说,从JVM入门到实战调优,涉及了Java程序从编译到执行的全过程,包括Class文件格式、类加载、内存模型、运行时数据区、垃圾收集策略以及调优实践等多个层面。深入理解和掌握这些知识,不仅有助于提升Java程序...

    JVM 虚拟机原理、内存结构、优化、垃圾回收分析等全方位的讲解,专业!

    本文将深入探讨JVM的核心原理、内存结构、优化策略以及垃圾回收机制。 首先,JVM的内存结构是理解其工作方式的关键。在JVM中,内存主要分为五个区域:程序计数器、虚拟机栈、本地方法栈、堆和方法区(在Java 8之后...

    jvm paper jvm

    二、JVM内存模型 1. 堆(Heap):所有对象都在堆中分配内存,是线程共享的区域,分为新生代和老年代。 2. 新生代(Young Generation):新生代进一步划分为Eden区和两个Survivor区(From空间和To空间)。大部分对象...

    基于Java虚拟机内存模型的性能调优方法.zip

    总结来说,基于Java虚拟机内存模型的性能调优涉及到多个层面,包括理解内存结构、垃圾回收机制、内存分配策略、调优工具的使用以及JVM参数的设置。通过深入理解和实践这些知识,开发者能够有效地优化Java应用程序,...

    JVM大厂面试题目集锦、垃圾回收、内存优化、内存结构全方位题目(附答案)

    JVM还提供了如CMS和G1等更复杂的垃圾收集器,CMS适用于响应时间敏感的应用,G1则试图平衡吞吐量和响应时间,两者都采用并发收集。 面试中常见的问题包括GC收集器的对比,如CMS收集器在内存不足时可能导致Full GC,...

Global site tag (gtag.js) - Google Analytics