JVM参数简介
-XX参数被称为不稳定参数,之所以这么叫是因为此类参数的设置很容易引起JVM 性能上的差异,使JVM 存在极大的不稳定性。如果此类参数设置合理将大大提高JVM 的性能及稳定性。
例如:-XX:+PrintGCDetail,-XX:+ParallelGC
不稳定参数语法规则
布尔类型参数值
-XX:+ '+'表示启用该选项
-XX:- '-'表示关闭该选项
数字类型参数值:
-XX:= 给选项设置一个数字类型值,可跟随单位,
例如:'m’或’M’表示兆字节;'k’或’K’千字节;'g’或’G’千兆字节。32K与32768是相同大小的。-XX:MaxMetaspaceSize=1000m、-XX:newRadio=3
字符串类型参数值
-XX:= 给选项设置一个字符串类型值,通常用于指定一个文件、路径或一系列命令列表。
例如:-XX:HeapDumpPath=./dump.core
FullGC出现前后打印日志
当出现了FullGC的时候,最需要我们注意的就是如何在指定的时间点,进行生产对应的heap dump文件以及对应的当前的快照信息。
JVM参数实现在Full GC前后自动生成Dump。共有三个VM参数需要设置:
1.HeapDumpBeforeFullGC 实现在Full GC前dump。
2.HeapDumpAfterFullGC 实现在Full GC后dump。
3.HeapDumpPath设置Dump保存的路径
设置这些参数的方法,这里总结了四种,大家可以根据情况选择使用。
方法1
启动jvm时,带上这些参数(这个方法适合开发测试环境)
Java -Xms200m -Xmx200m -Xmn50m -XX:PermSize=30m -XX:+HeapDumpBeforeFullGC -XX:+HeapDumpAfterFullGC -XX:HeapDumpPath=e:\dump -jar xxx.jar
方法2
使用JConsole等工具调用JMX服务的com.sum.management.HotSpotDiagnostic.setVMOption方法来实现。
第一个参数为HeapDumpBeforeFullGC, 第二个参数为true表示在Full GC前进行dump.
第一个参数为HeapDumpAfterFullGC, 第二个参数为true表示在Full GC前进行dump.
方法3
使用 **jinfo **命令进行设置。(生产环境常用的方法)
调用jinfo命令设置VM参数
# jinfo -flag +HeapDumpBeforeFullGC 5940
# jinfo -flag +HeapDumpAfterFullGC 5940
方法4
开发程序调用JMX的API来实现,得到了Full GC前后的dump, 接下来就可以使用一些分析工具(如MAT)来分析Full GC产生的原因了。
Java堆溢出
堆是用来存储对象的,如果创建了大量的对象且这些对象得不到及时的回收就会造成内存占满,抛出OOM(GC回收时间超过阈值的百分之98% 并且回收的内存不到2%)。
怎么解决
设置JVM参数,Dump出当前的内存堆转储快照
解决办法见思路
虚拟机栈和本地方法栈溢出
对单个线程来说:HotSpot将两个栈合二为一,栈里面存储的是栈帧,只有当栈帧不断压入栈(函数递归)就会栈溢出,此时栈深度过深:StackOverflowError。或者一个方法的变量太多导致栈帧内存太大,是的栈的内存不足,很快溢出OutOfMemoryError。
内存溢出:就是栈所占用内存和数量太多了,同时也侧面看出来创建的线程数量太多了,导致OOM。
解决办法
单线程来说:避免栈溢出
1. 找到递归函数,减少递归次数
2. 减少函数本地变量个数以及大小,相应的缩小栈帧的大小
3. 通过-Xss命令来扩大栈内存容量
多线程造成的OOM
1. 减少线程创建数量,优先使用线程池
2. 如果是在不能减少线程,可以适当的减少虚拟机栈的内存来增加可容纳的线程数
方法区和运行时常量池溢出
方法区存放的是类信息和运行时常量池
如果创建大量的类
(因为类的回收要求比较严格,所以一旦创建大量的类,得不到及时回收就会内存溢出)在项目中使用动态代理CGlib技术会生成大量的类,很多动态语言都是这么实现的,因为类加载机制,不同的加载器加载出的同一个类,JVM会认为不同且都保留。
如果创建大量的常量且放入常量池
String类中的intern()方法就是一个Native方法,它的作用是:如果字符串常量池中已经包含一个String对象的字符串(地址),则返回代表池中这个字符串的String对象地址;否则,将此String对象包含的字符串添加到heap堆中,并且返回此字符串常量池的String对象的引用。
直接内存溢出
DirectMemory容量可以通过-XX:MaxDirectMemorySize指定,如果不指定,则默认与Java堆最大值(-Xmx指定)一样。
虽然使用DirectByteBuffer分配内存也会抛出内存溢出异常,但它是并没有真正向操作系统申请分配内存,而是通过计算得知内存无法分配,手动抛出的内存异常。
-XX:MaxDirectoryMemorySize
-XX:MaxMetaSpaceSize
-XX:NewSpaceSize/-Xmx
-Xss
-XX:+HeapDumpOnOutOfMemoryError
在运行的时候设置JVM参数,使其Dump出内存异常信息
-XX:+HeapDumpOnOutOfMemoryError,当出现OOM异常的时候,我们就可以看到一场JVM打印的异常信息
通过内存映像分析工具(Eclipse Memory Analyzer)对Dump出来的内存转储快照进行分析
内存泄漏(Memory Leak)
内存溢出(Memory Overflow)
确定是哪种情况之后
如果是内存泄漏,可进一步通过工具查看泄漏对象到GC Roots的引用链,看看对象时通过怎样的路径与GC Roots相关联导致垃圾收集器无法自动回收他们,掌握了泄露对象的类型信息以及GC Roots引用链信息,就可以比较准确的定位泄漏的位置。
如果不存在泄漏,就说明这些对象都是应该存活的,但是内存不够大。这时候就需要调整堆内存的大小,对比机器物理内存看看堆内存能否相应的调大一点,从代码上检查是否存在某些对象生命周期过长、持有状态时间过长的情况,尝试减少程序运行期的内存消耗。