JVM运行参数

Java JVM内存介绍

JVM管理两种类型的内存,堆和非堆。按照官方的说法:“Java 虚拟机具有一个堆,堆是运行时数据区域,所有类实例和数组的内存均从此处分配。堆是在 Java 虚拟机启动时创建的。”“在JVM中堆之外的内存称为非堆内存(Non-heap memory)”。简单来说堆就是Java代码可及的内存,是留给开发人员使用的;非堆就是JVM留给自己用的,所以方法区、JVM内部处理或优化所需的内存(如JIT编译后的代码缓存)、每个类结构(如运行时常数池、字段和方法数据)以及方法和构造方法的代码都在非堆内存中,它和堆不同,运行期内GC不会释放其空间。

堆内存分配

JVM初始分配的内存由-Xms指定,默认是物理内存的1/64;JVM最大分配的内存由-Xmx指定,默认是物理内存的1/4。默认空余堆内存小于 40%时,JVM就会增大堆直到-Xmx的最大限制;空余堆内存大于70%时,JVM会减少堆直到-Xms的最小限制。因此服务器一般设置-Xms、 -Xmx相等以避免在每次GC 后调整堆的大小。可以利用JVM提供的-Xmn -Xms -Xmx等选项可进行堆内存设置,一般的要将-Xms和-Xmx选项设置为相同,而-Xmn为1/4的-Xmx值,建议堆的最大值设置为可用内存的最大值的80%。

初始化堆的大小是JVM在启动时向系统申请的内存的大小。一般而言,这个参数不重要。但是有的应用程序在大负载的情况下会急剧地占用更多的内存,此时这个参数就是显得非常重要,如果JVM启动时设置使用的内存比较小而在这种情况下有许多对象进行初始化,JVM就必须重复地增加内存来满足使用。由于这种原因,我们一般把-Xms和-Xmx设为一样大,而堆的最大值受限于系统使用的物理内存。一般使用数据量较大的应用程序会使用持久对象,内存使用有可能迅速地增长。当应用程序需要的内存超出堆的最大值时JVM就会提示内存溢出,并且导致应用服务崩溃。所以,如果Xms超过了Xmx值,或者堆最大值和非堆最大值的总和超过了物理内存或者操作系统的最大限制都会引起服务器启动不起来。

非堆内存分配

也叫永久保存的区域,用于存放Class和Meta信息,Class在被Load的时候被放入该区域。它和存放类实例(Instance)的Heap区域不同,GC(Garbage Collection)不会在主程序运行期对PermGen space进行清理。JVM使用-XX:PermSize设置非堆内存初始值,默认是物理内存的1/64;由XX:MaxPermSize设置最大非堆内存的大小,默认是物理内存的1/4。 GC不会对PermGen space进行清理,所以如果你的APP会LOAD很多CLASS的话,就很可能出现PermGen space错误。

JVM内存限制(最大值)

首先JVM内存限制于实际的最大物理内存,假设物理内存无限大的话,JVM内存的最大值跟操作系统有很大的关系。简单的说就32位处理器虽然可控内存空间有4GB,但是具体的操作系统会给一个限制,这个限制一般是2GB-3GB(一般来说Windows系统下为1.5G-2G,Linux系统 下为2G-3G),而64bit以上的处理器就不会有限制了。

三种内存溢出异常介绍

OutOfMemoryError:Java heap space 堆溢出

内存溢出主要存在问题就是出现在这个情况中。当在JVM中如果98%的时间是用于GC且可用的 Heap size 不足2%的时候将抛出此异常信息。

OutOfMemoryError:PermGen space 非堆溢出(永久保存区域溢出)

这种错误常见在web服务器对JSP进行pre compile的时候。如果你的WEB APP下都用了大量的第三方jar, 其大小超过了jvm默认的大小(4M)那么就会产生此错误信息了。如果web app用了大量的第三方jar或者应用有太多的class文件而恰好MaxPermSize设置较小,超出了也会导致这块内存的占用过多造成溢出,或者tomcat热部署时侯不会清理前面加载的环境,只会将context更改为新部署的,非堆存的内容就会越来越多。

OutOfMemoryError:unable to create new native thread. 无法创建新的线程

这种现象比较少见,也比较奇怪,主要是和jvm与系统内存的比例有关。这种怪事是因为JVM已经被系统分配了大量的内存(比如1.5G),并且它至少要占用可用内存的一半。

Java JVM内存配置

在Linux下设置Tomcat的java虚拟机内存

  • 查看catalina.sh
1
2
3
4
5
121 if [ -r "$CATALINA_BASE/bin/setenv.sh" ]; then
122 . "$CATALINA_BASE/bin/setenv.sh"
123 elif [ -r "$CATALINA_HOME/bin/setenv.sh" ]; then
124 . "$CATALINA_HOME/bin/setenv.sh"
125 fi
  • 在tomcat的bin目录下查看是否有setenv.sh,如果没有则创建,然后添加如下内容
1
export JAVA_OPTS='-XX:PermSize=128m -XX:MaxPermSize=256m -Xms512m -Xmx1024m -Xmn386M -Xss228k -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=8 -XX:ParallelGCThreads=4 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled -XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled -XX:CMSInitiatingOccupancyFraction=70 -XX:CMSFullGCsBeforeCompaction=5 -XX:+UseCMSCompactAtFullCollection -XX:-HeapDumpOnOutOfMemoryError -Xloggc:/srv/tomcat-forum-topic/logs/gc.log -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCApplicationStoppedTime -XX:+PrintGCApplicationConcurrentTime -Xdebug -Xrunjdwp:transport=dt_socket,address=10136,server=y,suspend=n -Djava.rmi.server.hostname=192.168.3.57 -Dcom.sun.management.jmxremote.port=10009 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false'

参数说明

  • -XX:PermSize:设定内存的永久保存区域;
  • -XX:MaxPermSize:设定最大内存的永久保存区域;
  • -Xms: Java Heap初始值,Server端JVM最好将-Xms和-Xmx设为相同值,开发测试机JVM可以保留默认值;
  • -Xmx: Java Heap最大值,默认值为物理内存的1/4;
  • -Xmn: Java Heap Young区大小,不熟悉最好保留默认值,一般设置为Xmx的3、4分之一;
  • -Xss: 每个线程的Stack大小,不熟悉最好保留默认值;
  • -XX:NewSize:设置JVM堆的‘新生代’的默认大小;
  • -XX:MaxNewSize:设置JVM堆的‘新生代’的最大大小;
  • -XX:SurvivorRatio:Eden区与Survivor区的大小比值;设置为8,则两个Survivor区与一个Eden区的比值为2:8,一个Survivor区占整个年轻代的1/10
  • -XX:MaxTenuringThreshold:垃圾最大年龄;如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代. 对于年老代比较多的应用,可以提高效率.如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活时间,增加在年轻代即被回收的概率。该参数只有在串行GC时才有效.
  • -XX:ParallelGCThreads:并行收集器的线程数;此值最好配置与处理器数目相等 同样适用于CMS
  • -XX:+UseConcMarkSweepGC:使用CMS内存收集;
  • -XX:+UseParNewGC:设置年轻代为并行收集;可与CMS收集同时使用
  • -XX:+CMSParallelRemarkEnabled:降低标记停顿
  • -XX:+CMSClassUnloadingEnabled:显示在使用CMS GC时,未加载类是否可用。JVM进行GC时便会清除永久代,并且删除不再使用的类,这个选项只会在UseConcMarkSweepGC 可用时才起作用。
  • -XX:+CMSPermGenSweepingEnabled:显示清除永久代是否可用。默认情况下这个参数是不可用的,所以要协调永久代问题,就必须显示设置这个参数。这个参数在Java 6里面被删除,所以如果你在使用Java 6或以上版本,你将不得不使用 -XX:+CMSClassUnloadingEnabled 选项。
  • -XX:CMSInitiatingOccupancyFraction=70:使用cms作为垃圾回收,使用70%后开始CMS收集
  • -XX:CMSFullGCsBeforeCompaction=5 :多少次后进行内存压缩;由于并发收集器不对内存空间进行压缩,整理,所以运行一段时间以后会产生”碎片”,使得运行效率降低.此值设置运行多少次GC以后对内存空间进行压缩,整理.
  • -XX:+UseCMSCompactAtFullCollection:在FULL GC的时候,对年老代的压缩;CMS是不会移动内存的,因此非常容易产生碎片,导致内存不够用,因此,内存的压缩这个时候就会被启用。
  • -XX:HeapDumpOnOutOfMemoryError:当发生OutOfMemoryError错误时,才能触发-XX:HeapDumpOnOutOfMemoryError 输出到-XX:HeapDumpPath指定位置。
  • -Xloggc:/gc.log:指定垃圾收集日志文件
  • -XX:+PrintGCDetails:每次GC时打印详细信息
  • -XX:+PrintGCTimeStamps:GC发生的时间
  • XX:+PrintGCApplicationStoppedTime:GC消耗了多少时间
  • XX:+PrintGCApplicationConcurrentTime:GC之间运行了多少时间
  • -Xdebug -Xrunjdwp:transport=dt_socket,address=10136,server=y,suspend=n:方便客户端远程调试
1
2
3
4
5
6
7
8
9
10
-XDebug 启用调试。
-Xnoagent 禁用默认sun.tools.debug调试器。
-Djava.compiler=NONE 禁止 JIT 编译器的加载。
-Xrunjdwp 加载JDWP的JPDA参考执行实例。
transport 用于在调试程序和 VM 使用的进程之间通讯。
dt_socket 套接字传输。
dt_shmem 共享内存传输,仅限于 Windows。
server=y/n VM 是否需要作为调试服务器执行。
address=3999 调试服务器的端口号,客户端用来连接服务器的端口号。
suspend=y/n 是否在调试客户端建立连接之后启动 VM 。
  • -Djava.rmi.server.hostname=192.168.3.57:指定ip
  • -Dcom.sun.management.jmxremote.port=10009:指定端口
  • -Dcom.sun.management.jmxremote.ssl=false:指定是否需要密码验证
  • -Dcom.sun.management.jmxremote.authenticate=false:指定是否使用 SSL 通讯

备注:JMX(Java Management Extensions)是一个为应用程序植入管理功能的框架,通过使用JMX,我们可以实时查询应用程序中通过JMX向外公布的相应参数或者是其他应用数据;可以使用Jconsole监控

参考

Tomcat中JVM内存溢出及合理配置

JVM系列三:JVM参数设置、分析

热评文章