`

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

阅读更多
来自:http://yuquan-nana.iteye.com/blog/599750

三  垃圾收集策略配置

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:U seParallelOldGC 来配置

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         - X X:+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:P arallelGCThreads=4    - X X:+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:P arallelGCThreads=4    - X X:+UseConcMarkSweepGC        -XX:+UseParNewGC       -XX:CMSIn itiatingOccupancyFraction=35

 

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

4.1 Minor GC的触发

Minor GC主 要负责收集 Young Generation Minor 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.OutOfMemory java 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 的空间大小为 10M Old 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 表示栈空间最大为 256 K.我们也可以调大,但是建议不 要对此参数进行调节。

4.3.2  OutOfMemoryError: Java heap space.

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

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

4.3.3  OutOfMemory:PermGen space

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

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

 

参考资料

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

分享到:
评论

相关推荐

    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