`

JVM参数对J2EE性能优化的影响

    博客分类:
  • Java
阅读更多
原博文地址【http://sinckyzhang.blog.sohu.com/149067215.html

 

 

    一切J2EE应用都是基于JVM的,那么对于JVM的设置和监控,成为J2EE应用程序性能分析和性能优化的必然手段。今天Sincky和大家交流该话题。这里以Tomcat环境为例,其它WEB服务器如Jboss、Weblogic、Websphere完全一致。

【认识JVM】

    首先我们来看一张图,这是目前JDK1.6版本自带的JVM性能监控工具VisualVM的一个插件VisualGC的显示情况。让我们先来了解JVM的内存堆Heap管理模式,要调整JVM,自然要知道它的内部结构和运作,此乃“知己知彼,百战不殆”!

 

 

 

    JVM的Heap包括三部分,分别是Permanent Generation(简称PermGen)、New Generation和Tenured Generation(又叫Old )

    PermGen是JVM自用的区域,是内存的永久保存区,用于存放反射代理和Class,Class在被装载时就会被放到PermGen中;所以如果WEB服务器里应用的Class相当多时,就可以考虑将这一块区域放大一些。

    New和Old是Java应用的Heap区,用来存放类的实例Instance的;其中New Generation的目标就是尽可能快速的收集掉那些生命周期短的对象;New Generation分为Eden Space,From Space和To Space三块,Eden Space用于存放新创建的对象,From区和To区都是救助空间Survivor Space(图中的S0和S1);当Eden区满时,JVM执行垃圾回收GC(Garbage Collection),垃圾收集器暂停应用程序,并会将Eden Space还存活的对象复制到当前的From救助空间,一旦当前的From救助空间充满,此区的存活对象将被复制到另外一个To区,当To区也满了的时候,从From区复制过来并且依然存活的对象复制到Old区,从而From和To救助空间互换角色,维持活动的对象将在救助空间不断复制,直到最终转入Old域。需要注意,Survivor的两个区是对称的,没先后关系,所以同一个区中可能同时存在从Eden复制过来对象,和从前一个Survivor复制过来的对象,而且,Survivor区总有一个是空的。同时,根据程序需要,Survivor区是可以配置为多个的(多于两个),这样可以增加对象在年轻代中的存在时间,减少被放到年老代的可能。每一次垃圾回收后Eden Space都会被清空。

    Old区用于存放长寿的对象,在New区中经历了N次垃圾回收后仍然存活的对象,就会被放到Old区中;如那些与业务信息相关的对象,包括Http请求中的Session对象、线程、Socket连接,这类对象跟业务直接挂钩,因此生命周期比较长。当任何一个空间不够用时,都会促使JVM执行垃圾回收而垃圾回收也需要消耗一定的时间,从而造成应用程序卡住;因此,合理的设置各个区的大小,可以进行快速GC即Mimor GC,避免频繁的Full GC

    所谓Minor GC,一般情况下,当新对象生成,并且在Eden申请空间失败时,就会触发Minor GC,对Eden区域进行GC,清除非存活对象,并且把尚且存活的对象移动到Survivor区,然后整理Survivor的两个区。这种方式的GC是对New区的Eden区进行,不会影响到Old区。因为大部分对象都是从Eden区开始的,同时Eden区不会分配的很大,所以Eden区的Minor GC会频繁进行。因而,一般在这里需要使用速度快、效率高的算法,使Eden去能尽快空闲出来。

    而Full GC要对整个Heap区进行回收,包括New、Old和PermGen,所以比Minor GC要慢,因此应该尽可能减少Full GC的次数。在对JVM性能调优的过程中,很大一部分工作就是对于Full GC的调节。有如下原因可能导致Full GC:
·Tenured区被写满
·PermGen区被写满
·System.gc()被显示调用
·上一次GC之后Heap的各域分配策略动态变化

【JVM参数实例一】JVM PermGen溢出:

java.lang.OutOfMemoryError  PermGen space

    由于GC不会在主程序运行期对PermGen进行清理,所以如果你的应用中有很多CLASS的话,就很可能出现PermGen space错误,这种错误常见在WEB服务器对JSP进行预编译的时候发生。如果你的WEB APP下都用了大量的第三方JAR包, 其大小超过了JVM默认的大小(4M)那么就会产生此错误信息了。

    解决方法: 手动设置MaxPermSize大小。修改TOMCAT_HOME/bin/catalina.bat,在echo Using CATALINA_base:%CATALINA_base%上面加入以下行:

JAVA_OPTS=%JAVA_OPTS% -server -XX:PermSize=64M -XX:MaxPermSize=128m

建议:将相同的第三方jar文件移置到tomcat/shared/lib目录下,这样可以达到减少jar 文档重复占用内存的目的。

【JVM参数实例二】JVM Heap溢出:

java.lang.OutOfMemoryError: Java heap space

     JVM Heap堆的设置是指java程序运行过程中JVM可以调配使用的内存空间。JVM在启动的时候会自动设置Heap size的值,其初始空间(即-Xms)是物理内存的1/64,最大空间(-Xmx)是物理内存的1/4。可以利用JVM提供的-Xmn -Xms -Xmx等选项可进行设置。提示:在JVM中如果98%的时间是用于GC且可用的Heap大小不足2%的时候将抛出此异常信息。Heap最大不要超过可用物理内存的80%,一般的要将-Xms和-Xmx选项设置为相同,而-Xmn为1/4的-Xmx值。 
    解决方法:手动设置Heap size,例如:

JAVA_OPTS=%JAVA_OPTS% -server -Xms800m -Xmx800m

    但是有的时候可能这样的设置还会不生效,例如当JVM加载大量Class类时,永久域中的对象急剧增加,从而

使JVM不断调整永久域大小。为了避免自动调整,可以使用前面的参数结合设置,如:

JAVA_OPTS=%JAVA_OPTS% -server -Xms800m -Xmx800m -XX:PermSize=64m -XX:MaxPermSize=128m

    基于Sun 的Java的垃圾回收机制,允许分隔内存池以包含不同时效的对象,垃圾回收循环根据时效收集与其他对象彼此独立的对象。使用其他参数,您可以单独设置内存池的大小。为了实现更好的性能,可以对包含短期存活对象的池的大小进行设置,以使该池中的对象的存活时间不会超过一个垃圾回收循环。新生成的池的大小由 NewSize和MaxNewSize参数确定。如:

JAVA_OPTS=%JAVA_OPTS% -server -Xms800m -Xmx800m  -XX:PermSize=64m -XX:MaxPermSize=128m -XX:NewSize=8m -XX:MaxNewSize=16m

【JVM参数实例三】垃圾回收时promotion failed:

    这个问题一般由两种原因引起,第一个原因是Survivor空间不够,里面的对象还不应该被移动到Old区,但New区又有很多对象需要放入Survivor;第二个原因是Old区没有足够的空间接纳来自New区的对象;这两种情况都会转向Full GC,造成系统卡住时间较长。第一个原因可以通过去掉救助空间解决,设置-XX:SurvivorRatio=65536 -XX:MaxTenuringThreshold=0即可,第二个原因可以设置CMSInitiatingOccupancyFraction为某个值(假设70),这样Old区的70%时就开始执行CMS,Old区有足够的空间接纳来自New的对象。但是不管怎样,PermGen还是会逐渐变满,所以隔三差五重起java服务器是必要的。需要注意的是,Old区用的是并发回收,可以设置New区小一点,Old区大些,可以减少系统的卡住。

    解决方法:

$JAVA_ARGS .= " -Dresin.home=$SERVER_ROOT -server -Xms6000M -Xmx6000M -Xmn500M -XX:PermSize=500M -XX:MaxPermSize=500M -XX:SurvivorRatio=65536 -XX:MaxTenuringThreshold=0 -Xnoclassgc -XX:+DisableExplicitGC -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=0 -XX:+CMSClassUnloadingEnabled -XX:-CMSParallelRemarkEnabled -XX:CMSInitiatingOccupancyFraction=90 -XX:SoftRefLRUPolicyMSPerMB=0 -XX:+PrintClassHistogram -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintHeapAtGC -Xloggc:log/gc.log"

-XX:SurvivorRatio=65536 -XX:MaxTenuringThreshold=0 就是去掉了救助空间
-Xnoclassgc 禁用类垃圾回收,性能会高一点
-XX:+DisableExplicitGC 禁止System.gc(),免得程序员误调用gc方法影响性能
-XX:+UseParNewGC 对New采用多线程并行回收,这样收得快
带CMS参数的都是和并发回收相关的:
CMSInitiatingOccupancyFraction,这个参数设置有很大技巧,基本上满足(Xmx-Xmn)*(100-CMSInitiatingOccupancyFraction)/100>=Xmn 就不会出现promotion failed。在这里Xmx是6000,Xmn是500,那么Xmx-Xmn是5500m,也就是Old有5500m,CMSInitiatingOccupancyFraction=90说明Old到90%满的时候开始执行对Old区的并发垃圾回收(CMS),这时还剩10%的空间是5500*10%=550m,所以即使Xmn(也就是New共500m)里所有对象都搬到Old里,550兆的空间也足够了,所以只要满足上面的公式,就不会出现垃圾回收时的promotion failed。
SoftRefLRUPolicyMSPerMB这个参数是softly reachable objects will remain alive for some amount of time after the last time they were referenced. The default value is one second of lifetime per free megabyte in the heap。

    但是,这种设置方法因为没有用到Survivor,所以Old区容易满,CMS执行会比较频繁。建议还是用Survivor并把加大,这样也不会有promotion failed错误。配置上32位和64位不一样,64位系统只要配置MaxTenuringThreshold参数,CMS还是有暂停。为了解决暂停问题和promotion failed问题,最后我设置-XX:SurvivorRatio=1 ,并把MaxTenuringThreshold去掉,这样即没有暂停又不会有promotoin failed,而且更重要的是,Old区和PermGen上升非常慢(因为好多对象到不了年老代就被回收了),所以CMS执行频率非常低,好几个小时才执行一次,这样,服务器都不用重启了。下面是64位的配置,系统8G内存。

-Xmx4000M -Xms4000M -Xmn600M -XX:PermSize=500M -XX:MaxPermSize=500M -Xss256K -XX:+DisableExplicitGC -XX:SurvivorRatio=1 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=0 -XX:+CMSClassUnloadingEnabled -XX:LargePageSizeInBytes=128M -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=80 -XX:SoftRefLRUPolicyMSPerMB=0 -XX:+PrintClassHistogram -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintHeapAtGC -Xloggc:log/gc.log

【JVM启动参数介绍】

-Xmn  -Eden Generation的Heap大小,一般设置为Xmx的1/3或1/4

-Xmx  -设置JVM Heap大小最大值,这里的heap = New Generation + Old Generation,但不包括PermGen

-Xms  -设置JVM Heap大小初始值

-XX:NewRatio -New/Old的大小比率

-XX:NewSize -New Generation Heap的大小

-XX:MaxNewSize -可以通过NewRatio和-Xmx计算得到

-XX:SurvivorRatio -Eden/Survivor Space大小比率

-XX:PermSize -PermGen的初始值

-XX:MaxPermSize -PermGen最大值

-Xss: -设置每个线程的Stack大小

-XX:+UseParNewGC  -表示多CPU下缩短Minor GC的时间

-XX:+UseParallelGC  -设置后可以使用并行清除收集器【多CPU】

-XX:+ParallelGCThreads  -可用来增加并行度【多CPU】

-XX:+AggressiveOpts  -是否激活最近的试验性性能调整

-XX:-Xnoclassgc  -是否允许类垃圾收集,默认设置是允许类 GC

-XX:+UseLargePages  -是否支持大页面堆

-XX:+UseFastAccessorMethods  -在指定了这个参数后,JDK会将所有的get/set方法都转为本地代码

-XX:+UseConcMarkSweepGC  -缩短major收集的时间,此选项在Heap比较大而且Full GC时间较长的情况下使用更合适

    另外,JVM的一些参数可以输出有效的日志文件:

-verbose:gc  -输出一些gc信息

-XX:+PrintGCDetails  -输出gc详细信息

-XX:+PrintGCTimeStamps  -包含时间戳信息

-XX:+PrintHeapAtGC  -包括gc前后Heap状况

-XX:+PrintTenuringDistribution  -输出对象存活时间和Tenured Generation的其他信息

-XX:+PrintHeapUsageOverTime  -以时间戳输出heap利用率和容量信息

-Xloggc:filename  -输出gc信息到日志文件
    例如:

set JAVA_OPTS=%JAVA_OPTS% -verbose:gc -Xloggc:gc.log -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintHeapAtGC

     如果输出的日志文件非常大,可以后续采用其它工具分析;或者干脆使用Jstat或Jconsole这些成熟的工具直接监控JVM的GC问题。当然,前边图示的VisualVM更是一款漂亮的、即时监控JVM GC的工具。

    下面的命令把整个堆设置成128m,New/Old比率设置成3,即New与Old比例为1:3,New Space为Heap的1/4即32M, 缺省值为NewSize=2m MaxNewSize=32m SurvivorRatio=2

java –Xms128m –Xmx128m –XX:NewRatio=3

    但是,如果JVM的堆Heap大小大于1GB,则应该使用值:-XX:newSize=640m -XX:MaxNewSize=640m -XX:SurvivorRatio=16,或者将堆的总大小的50%到60%分配给新生成的池。

【JVM性能瓶颈】

    在JAVA应用程序中,虽然不太容易出现内存泄漏的问题,因为JVM会不定期的进行垃圾回收。但是因为程序的不合理写法,也会导致一些数据不能被收集。典型的状况是在HashMap中放置大量不用的数据,而没有及时的清理。在web应用中,很多人喜欢在Session放放置状态数据,而没有清理,也是内存泄漏的一个原因。在Session中存放数据还好,因为Session终究会有过期时间,但是如果在Class的Static变量中放置数据,那就怎么样也没办法了。诊断应用中是否存在内存泄漏也有一些方法,通过分析JVM GC Log就是一个直观的方式。通过分析GC After Heap的变化趋势,如果GC After Heap稳步上升,立刻进行Full GC后,仍然不能降下来,通常就意味着存在内存泄漏了。当然也有情况是,的确有一些数据是应用程序的需求,我们要确认除了这些数据,是否还存在一些异常数据一直占据内存。可以通过JProbe的Memory Views来观察Class的对象数,在一段请求过后,如果还存在一些Class的Instance数目相当多,就可以判断这个Class可能会是问题的根源。

分享到:
评论

相关推荐

    J2EE性能优化.

    以下是一些关键的J2EE性能优化策略: 1. **应用服务器配置优化**: - 调整线程池大小:根据系统预期负载设置合适的最大和最小线程数量,避免过多或过少的线程导致资源浪费或响应延迟。 - 数据库连接池设置:合理...

    J2EE 性能优化

    以下是对J2EE性能优化的详尽探讨: 一、JVM(Java虚拟机)优化 1. **JVM内存配置**:理解不同JVM内存区域如堆内存(Heap)、方法区(Metaspace)、栈内存(Stack)等的作用,合理设置-Xms、-Xmx、-XX:...

    美河提供 J2EE性能测试

    以下是对J2EE性能测试的详细解释: 一、J2EE性能测试的定义 J2EE性能测试是针对基于J2EE技术的系统进行的一系列测试活动,旨在评估系统的响应时间、吞吐量、并发用户数和资源利用率等关键性能指标。这种测试有助于...

    使用PerformaSure进行J2EE性能监控及瓶颈分析

    "使用PerformaSure进行J2EE性能监控及瓶颈分析"这一主题,正是针对这一需求提供的一种专业解决方案。下面将详细介绍如何利用PerformaSure工具进行性能监控以及如何分析系统瓶颈。 首先,了解PerformaSure。...

    J2EE应用性能调优.docx

    性能调优--0:前言 2 性能调优--2:性能调优方法 4 性能调优--3:硬件网络层面调优 5 性能调优--4:操作系统层面调优 5 性能调优--5:J2EE容器层面调优 5 ...性能调优--10:一个项目的性能优化经验 16

    J2EE概要性能测试方法

    它要求在短时间内获取可重复的结果,以确定特定参数变更对性能的影响。例如,调整JVM内存大小,逐步增加并记录响应时间和系统资源使用情况。要获得可靠结果,每次只改变一个变量,确保测试的可比性。基准测试的结果...

    J2EE面试题集锦(附答案) - J2EE

    (如:调整JVM参数、使用缓存、优化数据库查询) - 解释负载均衡和集群的概念,以及它们在J2EE中的应用。 10. **开发工具与框架** - 常见的J2EE开发工具有哪些?(如:Eclipse、IntelliJ IDEA、NetBeans) - ...

    J2EE面试笔试常见题集大全

    11. **性能优化与调试**:面试可能会询问如何进行J2EE应用的性能调优,包括JVM参数调整、数据库优化、缓存策略等。 12. **安全性**:理解J2EE的安全机制,如JAAS(Java Authentication and Authorization Service)...

    j2ee个人博客系统

    在部署过程中,用户需要确保Tomcat服务器配置正确,包括JVM参数、上下文路径、数据库连接池配置等。同时,需要根据项目提供的配置文件(如web.xml、struts.xml、spring配置文件等)来调整应用的运行环境。在运行时,...

    Java性能优化技巧集锦

    Java性能优化是一个重要的主题,尤其是对于大型的企业级应用(如J2EE系统)而言,优化能够显著提升系统响应速度和整体效率。以下是一些关键的Java性能优化技巧: 1. **避免使用`new`关键字创建对象**: 在创建类实例...

    构建高性能J2EE应用的10个技巧xibu223

    构建高性能的J2EE应用是开发人员追求的目标,这涉及到一系列的策略...这包括JVM配置调整、数据库参数优化等。 以上这些技巧是构建高性能J2EE应用的关键步骤,但实际应用中还需要结合具体项目需求和技术环境灵活运用。

    构建J2ee应用程序

    13. **性能优化**: 调整JVM参数、使用缓存技术(如EHCache)、负载均衡和集群配置等都是提升J2EE应用性能的关键策略。 14. **持续集成与持续部署(CI/CD)**: 使用Git进行版本控制,通过Jenkins、Docker等工具实现...

    教你开发j2ee项目

    5. **JBoss配置小技巧**:这部分可能涵盖了如优化JVM参数、调整内存分配、日志配置等提高JBoss性能的方法。 6. **Web层通用技术**: - **Web容器运行机制**:这部分内容会介绍Servlet和JSP的工作原理,以及Web容器...

    JVM历史发展和内存回收笔记.rar

    5. **Java SE 5/6**:这两个版本引入了泛型、枚举、可变参数等新特性,并对JVM进行了进一步优化。 6. **Java SE 7/8**:JVM在这些版本中引入了 Lambda 表达式、默认方法、类型推断等特性,以及G1垃圾收集器,增强了...

    j2ee高级软件架构师培训资料内部资料-架构物理设计

    5. **性能调优**:资料会讲解如何通过调整JVM参数、应用服务器配置、缓存策略等来提升系统的性能和响应速度。 6. **安全性设计**:在J2EE环境中,安全问题至关重要。资料会介绍如何设置访问控制、身份验证、授权、...

    WEB服务器性能调优

    `WebLogic平台下J2EE调优攻略.doc`则可能深入讨论了如何针对J2EE应用程序进行性能优化,包括调整线程池、内存分配、垃圾收集策略等关键环节。 `WebLogic服务器性能参数.doc`文档可能会列出并解释WebLogic服务器的...

    j2ee通过jni调用dll

    这通常发生在以下情况:需要利用操作系统提供的低级别功能,如文件I/O、硬件控制或者优化性能的关键算法。 步骤如下: 1. **创建DLL**:使用C或C++编写本地代码,实现需要的功能,并将其编译成动态链接库(DLL)。...

    J2EE程序设计考试复习资料

    性能优化是J2EE应用开发的关键环节,包括JVM参数调整、内存管理、线程池配置、数据库连接池优化等。 综上所述,"J2EE考试复习试题"文档应涵盖这些关键知识点,通过做题和解答,考生可以全面检查自己的学习情况,...

    JVM深入理解及调优.ppt

    通过JDK提供的工具如`jconsole`、`jvisualvm`、`jmap`、`jstat`等可以收集性能数据,并基于这些数据调整JVM参数,以优化应用程序的性能。 了解和掌握这些知识点,可以帮助Java开发者更有效地编写和优化代码,提升...

    精品:java虚拟机分析与优化PPT

    为了能够使得您的J2EE应用性能取得上佳的效果,需要了解JVM是如何执行他的底层管理任务,并且如何根据您特定的工作服在进行性能调优。 在这个主题当中我们会一起来探索在WebSphere运行期JVM是如何运作的。我们将...

Global site tag (gtag.js) - Google Analytics