JVM参数详解及OOM
JVM参数
堆的限制
JVM中最大堆大小有三方面限制:
- 相关操作系统的数据模型(32-bt还是64-bit)限制
- 系统的可用虚拟内存限制
- 系统的可用物理内存限制
32位系统下,一般限制在1.5G~2G;64为操作系统对内存无限制
堆参数
- -Xmx: 指定JVM的最大堆大小,如:-Xmx=2g
- -Xms: 指定JVM的最小堆大小,如:-Xms=2g,高并发应用,建议和-Xmx一样,防止因为内存收缩/突然增大带来的性能影响。
- -Xmn: 指定JVM中NewGeneration的大小,如:-Xmn256m。这个参数很影响性能,如果程序需要比较多的临时内存,可以适当设置高点。
- -XX:PermSize: 指定JVM中PermGeneration的最小值,如:-XX:PermSize=32m,在Java8中此参数被忽略,永久代由元空间代替。
- -XX:MaxPermSize: 指定JVM中PermGeneration的最大值,如:-XX:MaxPermSize=64m,在Java8中此参数被忽略,永久代由元空间代替。
- -Xss: 指定线程桟大小,如:-Xss128k,一般来说,WEB框架下的应用需要256K,如果程序有大规模的递归行为,可以设置到512K/1M。这个需要全面的测试才能知道。不过,256K已经很大了。这个参数对性能的影响比较大的。
- -XX:NewRatio: 指定JVM中OldGeneration堆与NewGeneration的比例(OldSize/NewSize),在使用CMSGC(Concurrent Mark-Sweep GC)的情况下此参数失效,如:-XX:NewRatio=2
- -XX:SurvivorRatio: 指定NewGeneration中EdenSpace与一个SurvivorSpace的堆size比例,-XX:SurvivorRatio=8,那么在总共NewGeneration为10m的情况下,EdenSpace为8m
- -XX:MinHeapFreeRatio: 指定JVMheap在使用率小于n的情况下,heap进行收缩,Xmx==Xms的情况下无效,如:-XX:MinHeapFreeRatio=30
- -XX:MaxHeapFreeRatio: 指定JVMheap在使用率大于n的情况下,heap进行扩张,Xmx==Xms的情况下无效,如:-XX:MaxHeapFreeRatio=70
- -XX:LargePageSizeInBytes: 指定Java堆的分页页面大小,如:-XX:LargePageSizeInBytes=128m
元空间
持久代的空间被彻底地删除了,它被一个叫元空间的区域所替代了。持久代删除了之后,很明显,JVM会忽略PermSize和MaxPermSize这两个参数,还有就是你再也看不到java.lang.OutOfMemoryError: PermGen error的异常了。
JDK 8的HotSpot JVM现在使用的是本地内存来表示类的元数据,这个区域就叫做元空间,绝大多数的类元数据的空间都从本地内存中分配。
垃圾收集器参数
- -XX:+UseParallelGC: 指定在NewGeneration使用ParallelCollector,并行收集,暂停APPThreads,同时启动多个垃圾回收Thread,不能和CMSGC一起使用.系统吨吐量优先,但是会有较长时间的AppPause,后台系统任务可以使用此GC。
- -XX:ParallelGCThreads: 指定parallelcollection时启动的Thread个数,默认是物理Processor的个数,
- -XX:+UseParallelOldGC: 指定在OldGeneration使用ParallelCollector
- -XX:+UseParNewGC: 指定在NewGeneration使用ParallelCollector,是UseParallelGC的gc的升级版本,有更好的性能或者优点,可以和CMSGC一起使用
- -XX:+CMSParallelRemarkEnabled: 在使用UseParNewGC的情况下,尽量减少mark的时间
- -XX:+UseConcMarkSweepGC: 指定在OldGeneration使用CMSGC,GCThread和APPThread并行(在init-mark和remark时PauseAPPThread)。apppause时间较短,适合交互性强的系统,如webserver
- -XX:+UseCMSCompactAtFullCollection: 在使用ConcurrentGC的情况下,防止MemoryFragmention,对LiveObject进行整理,使Memory碎片减少
- -XX:CMSFullGCsBeforeCompaction: 上面配置开启的情况下,这里设置多少次FullGC后,对年老代进行压缩
- -XX:CMSInitiatingOccupancyFraction: 指示在oldgeneration在使用了n%的比例后,启动ConcurrentCollector,默认值是68,如:-XX:CMSInitiatingOccupancyFraction=70 有个bug,在低版本(1.5.09andearly)的JVM上出现,http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6486089
- -XX:+UseCMSInitiatingOccupancyOnly 指示只有在OldGeneration在使用了初始化的比例后ConcurrentCollector启动收集
其他
- -XX:MaxDirectMemorySize: 指定本地直接内存大小,如果不指定,则默认与Java堆的最大值-Xmx指定一样。
- -XX:MaxTenuringThreshold: 指定一个Object在经历了n次YoungGC后转移到OldGeneration区,在Linux64的Java6下默认值是15,此参数对于throughputcollector无效,如:-XX:MaxTenuringThreshold=31
- -XX:+DisableExplicitGC: 禁止Java程序中的FullgGC,如System.gc()的调用.最好加上,防止程序在代码里误用了。对性能造成冲击。
- -XX:+UseFastAccessorMethods: get,set方法转成本地代码
- -XX:+PrintGCDetails: 打应垃圾收集的情况如:[GC15610.466:[ParNew:229689K->20221K(235968K),0.0194460secs]1159829K->953935K(2070976K),0.0196420secs]
- -XX:+PrintGCTimeStamps: 打应垃圾收集的时间情况,如:[Times:user=0.09,sys=0.00,real=0.02secs]
- -XX:+PrintGCApplicationStoppedTime: 打应垃圾收集时,系统的停顿时间,如:Total-Time-For-Which-Application-Threads-Were-Stopped:0.0225920seconds
各种OOM/SOF程序
为了进一步理解这些参数,我将尝试写出各种OOM/SOF错误的程序,并附上步骤。
Heap Out Of Memory
要使堆溢出非常简单,只要在程序中强引用许多对象例如字符串即可。
输入命令行:
javac HeapOOM.javajava -Xmx1m -Xms1m HeapOOM
/**
* Created by decaywood on 16-1-5.
*/public class HeapOOM {
public static void main(String[] args) {
List<String> strings = new ArrayList<>();
System.out.println("doing....");
for(int i = 0;;i++) {
strings.add(String.valueOf(i));
}
}}
运行结果:
doing....Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3210)
at java.util.Arrays.copyOf(Arrays.java:3181)
at java.util.ArrayList.grow(ArrayList.java:261)
at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:235)
at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:227)
at java.util.ArrayList.add(ArrayList.java:458)
at HeapOOM.main(HeapOOM.java:13)
MethodArea Out Of Memory & ConstantPool Out Of Memory
由于运行时常量池是方法区的一部分,因此这两个区域的溢出测试就放在一起进行。
String.intern()是一个Native方法,它的作用是:如果字符串常量池中已经包含一个等于此String对象的字符串,则返回代表池中这个字符串的String对象;否则,将此String对象包含的字符串添加到常量池中,并且返回此String对象的引用。在JDK 1.6及之前的版本中,由于常量池分配在永久代内,我们可以通过-XX:PermSize和-XX:MaxPermSize限制方法区大小,从而间接限制其中常量池的容量。
javac MethodAreaOOM.javajava -XX:PermSize10m -XX:MaxPermSize10m MethodAreaOOM
不过这里要注意,Java8中虚拟机已将方法区移除,执行以上代码会得到如下信息:
java -XX:PermSize10m -XX:MaxPermSize10m MethodAreaOOM
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option PermSize10m; support was removed in 8.0Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize10m; support was removed in 8.0
/**
* Created by decaywood on 16-1-5.
*/public class MethodAreaOOM {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
int i = 0;
for (;;) {
list.add(String.valueOf(i++).intern());
}
}}
输入命令行:
javac MethodAreaOOM.javajava -XX:PermSize10m -XX:MaxPermSize10m MethodAreaOOM
运行结果:
Exception in thread "main" java.lang.OutOfMemoryError: PermGen space
at java.lang.String.intern(Native Method)
at org.fenixsoft.oom.RuntimeConstantPoolOOM.main(MethodAreaOOM.java:18)
从运行结果中可以看到,运行时常量池溢出,在OutOfMemoryError后面跟随的提示信息是“PermGen space”,说明运行时常量池属于方法区(HotSpot虚拟机中的永久代)的一部分。而使用JDK 1.7运行这段程序就不会得到相同的结果,while循环将一直进行下去。关于这个字符串常量池的实现问题,还可以引申出一个更有意思的影响。
/**
* Created by decaywood on 16-1-5.
*/public class StringTest {
public static void main(String[] args) {
String str1 = new StringBuilder("Programing").append("Language").toString();
System.out.println(str1.intern() == str1);
String str2 = new StringBuilder("ja").append("va").toString();
System.out.println(str2.intern() == str2);
}}
这段代码在JDK 1.6中运行,会得到两个false,而在JDK 1.7中运行,会得到一个true和一个false。产生差异的原因是:在JDK 1.6中,intern()方法会把首次遇到的字符串实例复制到永久代中,返回的也是永久代中这个字符串实例的引用,而由StringBuilder创建的字符串实例在Java堆上,所以必然不是同一个引用,将返回false。而JDK 1.7(以及部分其他虚拟机,例如JRockit)的intern()实现不会再复制实例,只是在常量池中记录首次出现的实例引用,因此intern()返回的引用和由StringBuilder创建的那个字符串实例是同一个。对str2比较返回false是因为“java”这个字符串在执行StringBuilder.toString()之前已经出现过,字符串常量池中已经有它的引用了,不符合“首次出现”的原则,而“计算机软件”这个字符串则是首次出现的,因此返回true。
方法区用于存放Class的相关信息,如类名、访问修饰符、常量池、字段描述、方法描述等。对于这些区域的测试,基本的思路是运行时产生大量的类去填满方法区,直到溢出。
/**
* Created by decaywood on 16-1-5.
*/public class MethodAreaOOM {
static class OOMObject {}
public static void main(String[] args) {
for (;;) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(OOMObject.class);
enhancer.setUseCache(false);
enhancer.setCallback(new MethodInterceptor() {
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
return proxy.invokeSuper(obj, args);
}
});
enhancer.create();
}
}}
输入命令行:
javac MethodAreaOOM.javajava -XX:PermSize10m -XX:MaxPermSize10m MethodAreaOOM
运行结果:
Caused by: java.lang.OutOfMemoryError: PermGen space
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClassCond(ClassLoader.java:632)
at java.lang.ClassLoader.defineClass(ClassLoader.java:616)
... 8 more
方法区溢出也是一种常见的内存溢出异常,一个类要被垃圾收集器回收掉,判定条件是比较苛刻的。在经常动态生成大量Class的应用中,需要特别注意类的回收状况。这类场景除了上面提到的程序使用了CGLib字节码增强和动态语言之外,常见的还有:大量JSP或动态产生JSP文件的应用(JSP第一次运行时需要编译为Java类)、基于OSGi的应用(即使是同一个类文件,被不同的加载器加载也会视为不同的类)等。在Java8中,由于绝大多数的类元数据的空间都从本地内存中分配,所以你再也看不到java.lang.OutOfMemoryError: PermGen error的异常了。
DirectMemory Out Of Memory
DirectMemory容量可通过-XX:MaxDirectMemorySize指定,如果不指定,则默认与Java堆最大值(-Xmx指定)一样,下面的代码直接通过反射获取Unsafe实例进行内存分配(Unsafe类的getUnsafe()方法限制了只有引导类加载器才会返回实例,也就是设计者希望只有rt.jar中的类才能使用Unsafe的功能)。因为,虽然使用DirectByteBuffer分配内存也会抛出内存溢出异常,但它抛出异常时并没有真正向操作系统申请分配内存,而是通过计算得知内存无法分配,于是手动抛出异常,真正申请分配内存的方法是unsafe.allocateMemory()。
/**
* Created by decaywood on 16-1-5.
*/public class DirectMemoryOOM {
private static final int _1MB = 1024 * 1024;
public static void main(String[] args) throws IllegalAccessException {
/* Retrieve Unsafe */
Field unsafeField = Unsafe.class.getDeclaredFields()[0];
unsafeField.setAccessible(true);
Unsafe unsafe = (Unsafe) unsafeField.get(null);
for (;;) {
unsafe.allocateMemory(_1MB);
}
}}
输入命令行:
javac DirectMemoryOOM.java
java -XX:MaxDirectMemorySize=5m DirectMemoryOOM
运行结果:
Exception in thread "main" java.lang.OutOfMemoryError
at sun.misc.Unsafe.allocateMemory(Native Method)
at org.fenixsoft.oom.DMOOM.main(DMOOM.java:20)
由DirectMemory导致的内存溢出,一个明显的特征是在Heap Dump文件中不会看见明显的异常,如果发现OOM之后Dump文件很小,而程序中又直接或间接使用了NIO,那就可以考虑检查一下是不是这方面的原因。
Stack Overflow
在Java开发中,栈溢出是最常见的错误,一般是由于递归过深导致,如果出现这个错误绝大部分情况是由于无限递归引起的,仔细检查跳出递归条件即可避免。如果确实递归深度过深,出于效率考虑,可以改写成非递归形式,实在不行,更改-Xss参数即可。
/**
* Created by decaywood on 16-1-5.
*/public class StackOverflow {
private static void recurse() {
recurse();
}
public static void main(String[] args) {
recurse();
}}
输入命令行:
javac StackOverflow.javajava -Xss228k StackOverflow
运行结果:
Exception in thread "main" java.lang.StackOverflowError
at StackOverflow.recurse(StackOverflow.java:13)
at StackOverflow.recurse(StackOverflow.java:13)
at StackOverflow.recurse(StackOverflow.java:13)
at StackOverflow.recurse(StackOverflow.java:13)
...
转自:http://blog.decaywood.me/2016/01/05/jvm-summary/
版权申明:内容来源网络,版权归原创者所有。除非无法确认,我们都会标明作者及出处,如有侵权烦请告知,我们会立即删除并表示歉意。谢谢。
- Silverlight本地化
- Nodejs学习笔记(五)--- Express安装入门与模版引擎ejs
- Silverlight本地化
- Silverlight 3 创建一个简单的Behavior
- Nodejs学习笔记(六)--- Node.js + Express 构建网站预备知识
- Silverlight 3.0 中的 Local Connection
- 学习Spark——那些让你精疲力尽的坑
- 学习Spark——那些让你精疲力尽的坑
- Silverlight 3.0 中的 WriteableBitmap
- WCF后续之旅(10): 通过WCF Extension实现以对象池的方式创建Service Instance
- Silverlight菜单控件 — CurveMenu
- 实力终端撑腰 两枚域名均五位数被秒
- Silverlight制作逐帧动画 v2 - part2
- Nodejs学习笔记(四)--- 与MySQL交互(felixge/node-mysql)
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- E: Sub-process /usr/bin/dpkg returned an error code (1) 解决方案
- Linux 如何使用包管理器安装 Node.js
- CSS画图
- R语言聚类算法的应用实例
- Python时间序列选择波动率预测指数收益算法分析案例
- Linux 常用系统工作命令-date
- R使用LASSO回归预测股票收益
- Linux 常用系统工作命令-reboot、poweroff、wget
- Node.js + Socket.io 实现一对一即时聊天
- Linux 常用系统工作命令-ps、top
- Python之LDA主题模型算法应用
- R语言highfrequency高频金融数据导入
- IIS配置优化
- R语言做复杂金融产品的几何布朗运动的模拟
- R语言数据的收益率和可能的波动性交易