一:直接上干货,一下参数可以直接用:jmap -heap 进程号来查看
-Xss:栈大小
-Xms:堆的最小值;
-Xmx:堆的最大值;
-Xmn:新生代的大小;
-XX:NewRatio 老年代(不包括永久区)与新生代(Eden + from+to)的比值;
要求比较的搞得吞吐量(避免较多的fullGC)4 表示新生代 :老年代 = 1:4 ,意思是老年代占 4/5
-XX:SurvivorRatio:8表示Eden区与Survivor区的大小比值是8:1:1,因为Survivor区有两个(from, to)
-XX:NewSize;新生代最小值;
-XX:MaxNewSize:新生代最大值
-XX:PermSize;-XX:MaxPermSize;方法区/永久代(dk1.7及以前)
-XX:MetaspaceSize; -XX:MaxMetaspaceSize 方法区/永久代 jdk1.8以后,大小就只受本机总内存的限制
-XX:MaxDirectMemorySize ;直接内存
类型 | 参数 |
运行模式 | -sever |
整个堆内存大小 | 为-Xms和-Xmx设置相同的值。 |
新生代空间大小 | -XX:NewRatio: 2到4. -XX:NewSize=? –XX:MaxNewSize=?. 使用NewSize代替NewRatio也是可以的。 |
持久代空间大小 | -XX:PermSize=256m -XX:MaxPermSize=256m. 设置一个在运行中不会出现问题的值即可,这个参数不影响性能。 |
GC日志 | -Xloggc:$CATALINA_BASE/logs/gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps. 记录GC日志并不会特别地影响Java程序性能,推荐你尽可能记录日志。 |
GC算法 | -XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=75. 一般来说推荐使用这些配置,但是根据程序不同的特性,其他的也有可能更好。 |
发生OOM时创建堆内存转储文件 | -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=$CATALINA_BASE/logs |
发生OOM后的操作 | -XX:OnOutOfMemoryError=$CATALINA_HOME/bin/stop.sh 或 -XX:OnOutOfMemoryError=$CATALINA_HOME/bin/restart.sh. 记录内存转储文件后,为了管理的需要执行一个合适的操作。 |
二:运行时数据区域
1:运行时数据区域:当前线程执行的字节码的行号指示器,占用空间小,也无法干涉
2:虚拟机栈:
每个线程私有的,线程在运行时,在执行每个方法的时候都会打包成一个栈帧,存储了局部变量表,操作数栈,动态链接,方法出口等信息,然后放入栈。每个时刻正在执行的当前方法就是虚拟机栈顶的栈桢。方法的执行就对应着栈帧在虚拟机栈中入栈和出栈的过程。栈桢大小缺省为1M,可用参数 –Xss调整大小,例如-Xss256k
3:堆:几乎所有对象都分配在这里,也是垃圾回收发生的主要区域
4:方法区/永久代:用于存储已经被虚拟机加载的类信息,常量("zdy","123"等),静态变量(static变量)等数据,可用以下参数调整
5:直接内存:
不是虚拟机运行时数据区的一部分,也不是java虚拟机规范中定义的内存区域;如果使用了NIO,这块区域会被频繁使用,在java堆内可以用directByteBuffer对象直接引用并操作;
这块内存不受java堆大小限制,但受本机总内存的限制,可以通过-XX:MaxDirectMemorySize来设置(默认与堆内存最大值一样),所以也会出现OOM异常
6:栈上分配
虚拟机提供的一种优化技术,基本思想是,对于线程私有的对象,将它打散分配在栈上,而不分配在堆上。好处是对象跟着方法调用自行销毁,不需要进行垃圾回收,可以提高性能。
栈上分配需要的技术基础,逃逸分析。逃逸分析的目的是判断对象的作用域是否会逃逸出方法体。注意,任何可以在多个线程之间共享的对象,一定都属于逃逸对象。
public void test(int x,inty ){
String x = “”;
User u = ….
…..
}
User类型的对象u就没有逃逸出方法test。
public User test(int x,inty ){
String x = “”;
User u = ….
…..
return u;
}
User类型的对象u就逃逸出方法test。
如何启用栈上分配
-server JVM运行的模式之一, server模式才能进行逃逸分析, JVM运行的模式还有mix/client
-Xmx10m和-Xms10m:堆的大小
-XX:+DoEscapeAnalysis:启用逃逸分析(默认打开)
-XX:+PrintGC:打印GC日志
-XX:+EliminateAllocations:标量替换(默认打开)
-XX:-UseTLAB 关闭本地线程分配缓冲
TLAB: ThreadLocalAllocBuffer,具体解释参见下文《虚拟机中的对象---对象的分配----2)》
对栈上分配发生影响的参数就是三个,-server、-XX:+DoEscapeAnalysis和-XX:+EliminateAllocations,任何一个发生变化都不会发生栈上分配,因为启用逃逸分析和标量替换默认是打开的,所以,在我们的例子中,JVM的参数只用-server一样可以有栈上替换的效果(以Mark老师机器上JDK1.8为例,其他版本JDK请自行尝试)
对象的内存布局
在HotSpot虚拟机中,对象在内存中存储的布局可以分为3块区域:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)。
对象头包括两部分信息,第一部分用于存储对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等。
对象头的另外一部分是类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。
第三部分对齐填充并不是必然存在的,也没有特别的含义,它仅仅起着占位符的作用。由于HotSpot VM的自动内存管理系统要求对对象的大小必须是8字节的整数倍。当对象其他数据部分没有对齐时,就需要通过对齐填充来补全
三:OOM案例分析
出现java.lang.OutOfMemoryError: GC overhead limit exceeded 一般是(某个循环里可能性最大)在不停的分配对象,但是分配的太多,把堆撑爆了。
出现java.lang.OutOfMemoryError: Java heap space一般是分配了巨型对象
栈溢出
参数:-Xss256k
java.lang.StackOverflowError 一般的方法调用是很难出现的,如果出现了要考虑是否有无限递归。
虚拟机栈带给我们的启示:方法的执行因为要打包成栈桢,所以天生要比实现同样功能的循环慢,所以树的遍历算法中:递归和非递归(循环来实现)都有存在的意义。递归代码简洁,非递归代码复杂但是速度较快。