1.Trace跟踪参数
①-verbose:gc表示输出虚拟机中GC的详细情况
②-XX:+printGC开启了简单GC日志模式,为每一次新生代(young generation)的GC和每一次的Full GC打印一行信息。
-XX:+PrintGCDetails 打印GC详细信息,只会在程序结束之后才会打印堆的相关信息
-XX:+PrintGCTimeStamps打印GC发生的时间戳
-新生代总共有13824K大小,已经使用了11223K
新生代=eden + survivor
-eden区空间为12288K,有91%被使用了
有两个大小相等的survivor区,from 和 to
-from区 有1536K 未被使用
-to区 有1536K 未被使用
-老年代tenured generation总共5120K,未被使用
-永久区的一个共享区 总共12288K,已使用142K,一些基础的java类会被加载到共享区中,供所有java虚拟机使用
-ro只读共享区大小10240K 使用了44%
-rw可读写共享区大小12288K,52%已使用
---------------------------------------------------------------------------------该部分由于首次接触,还没太熟悉,相信慢慢的会理解的
低边界:代表起始位置
当前边界:当前所被分配/所申请到的位置
最高边界:新生代能申请到的最高位置
(0x28d80000-0x27e80000)/1024/1024=15M表明新生代正好被分配了15M
15M = eden区大小+from区大小+to区大小;
新生代总共有13824K大小=eden + survivor = 12288+1536
-Xloggc:log/gc.log指定GC log的位置当前目录下log文件夹下面的路径,以文件输出,有助于帮助开发人员分析问题
-XX:+PrintHeapAtGC表示每次GC后都打印堆信息
GC调用前后的区别:eden区从100%被使用变成0%被使用,from区从0%used到100%used
老年代从0%uesed到已使用57k,部分数据从新生代移到了老年代。----------具体情况为什么会这样,还没了解透,不便解释
-XX:+TraceClassLoading 监控类的加载
2.堆的分配参数
-Xmx最大堆空间 Xms最小堆空间
public class TestHeap{
public static void main(String[] args){
System.out.println("maxMemory:"
+ Runtime.getRuntime().maxMemory()/1024.0/1024 + "M");
System.out.println("freeMemory:"
+ Runtime.getRuntime().freeMemory()/1024.0/1024 + "M");
System.out.println("totalMemory:"
+ Runtime.getRuntime().totalMemory()/1024.0/1024 + "M");
}
}
maxMemory()这个方法返回的是java虚拟机(这个进程)能够从操作系统那里挖到的最大的内存,以字节为单位,如果在运行java程序的时 候,没有添加-Xmx参数,那么就是64兆,这是java虚拟机默认情况下能从操作系统那里挖到的最大的内存。如果添加了-Xmx参数,将以这个参数后面的值为准。
totalMemory()这个方法返回的是java虚拟机现在已经从操作系统得到的内存大小,也就是java虚拟机这个进程当时所占用的所有内存。如果在运行java的时候没有添加-Xms参数,那么,在java程序运行的过程的,内存总是慢慢的从操作系统那里挖的,基本上是用多少分配多少,直到分配到maxMemory()为止,所以totalMemory()是慢慢增大的。如果用了-Xms参数,程序在启动的时候就会无条件的从操作系统中挖接近- Xms后面定义的内存数,然后在这些内存用的差不多的时候,再去申请空间。
freeMemory()是什么呢,刚才讲到如果在运行java的时候没有添加-Xms参数,那么,在java程序运行的过程的,内存总是慢慢的从操作系统那里申请的,基本上是用多少申请多少,但是java虚拟机100%的情况下是会稍微多挖一点的,这些挖过来而又没有用上的内存,实际上就是 freeMemory(),所以freeMemory()的值一般情况下都是很小的,但是如果你在运行java程序的时候使用了-Xms,这个时候因为程序在启动的时候就会无条件的从操作系统中申请-Xms后面定义的内存数,这个时候,申请过来的内存可能大部分没用上,所以这个时候freeMemory()可能会有些大。
上述代码中增加了该语句
byte[] b = new byte[1 * 1024 *1024];
byte[] b = new byte[5 * 1024 *1024];
将其增大后,原来该进程所申请的5.5M空间不够了,就会继续申请
byte[] b = new byte[5 * 1024 *1024];
System.gc();
//加入GC之后
-Xmn设置新生代的大小,表示新生代的绝对值
-XX:NewRatio 新生代(eden + 2*s)和老年代(不包含永久区)的比值,例如4表示新生代:老年代=1:4,年轻代占堆的1/5
-XX:SurvivorRatio
-设置两个Survivor区和eden的比
8表示(from + to):eden=2:8,即每个survivor占1/10
public class TestHeap2{
public static void main(String[] args){
byte[] b = null;
for(int i = 0; i < 10; i++){
b = new byte[1 * 1024 * 1024];
}
}
}
此时如果将-Xmn设置为1M,系统发出警告,当前分配的新生代大小1M过小了,系统会将其修改为1536k,将新生代大小改为2M就没有错误了。但是这个错误详细解释查阅很多资料没找到,表示有点迷糊。不清楚是不是Jdk1.8不能设置-Xmn为1M
java -Xmx20m -Xms20m -Xmn2m -XX:+PrintGCDetails TestHeap2
java -Xmx20m -Xms20m -Xmn2m -XX:+PrintGCDetails TestHeap2
使用该命令,内存申请大小虽然为1M,加上本身系统的资源,年轻代的2M无法满足需求,所以10M的内存分配都放到了老年代。
java -Xmx20m -Xms20m -Xmn3m -XX:+PrintGCDetails TestHeap2
此时新生代分配了3m空间,从上述两个堆分配中可知,内存申请为1M加上系统资源,资源内存处于2m-3m的范围,所以将申请的一个1m内存分配到了年轻代,其余9m的内存申请放到了老年代。此处没有发生GC有点疑惑。
java -Xmx20m -Xms20m -Xmn15m -XX:+PrintGCDetails TestHeap2
没有发生分配失败和GC回收。 10M的分配都进入了Eden区域。 年老代中为0, Survivior区域为0.
java -Xmx20m -Xms20m -Xmn7m -XX:+PrintGCDetails TestHeap2
发生两次GC,回收空间大约在5m左右,eden区分配了6M左右空间,from和to分配512k,不足以放下内存申请的1M空间,所以老年代还是有部分的内存被分配了(刚开始了解虚拟机,很多知识还不懂,到后面学了垃圾回收和内存分配,再回来看看这个情况)
java -Xmx20m -Xms20m -Xmn7m -XX:SurvivorRatio=2 -XX:+PrintGCDetails TestHeap2
此时新生代中的from和to,已经超过了内存申请的1m大小,可以被分配内存,所以出发了三次GC,每次回收大约3M,基本不会在老年代进行内存分配,加上系统级别对象资源的占用,所以老年代依然有内存使用状况。
java -Xmx20m -Xms20m -XX:NewRatio=1 -XX:SurvivorRatio=2 -XX:+PrintGCDetails TestHeap2
NewRatio为1, 则新生代和年老代的比例是1:1, 都为10M。 SurvivorRatio为2, 则2个survior:eden = 2:2, 平均分配, 5m, 2.5m, 2.5m
java -Xmx20m -Xms20m -XX:NewRatio=1 -XX:SurvivorRatio=4 -XX:+PrintGCDetails TestHeap2
增加eden区的大小,减少survivor区的大小能够减少GC的发生,GC次数下降后,很多新生代的对象就无法晋升到老年代了
-XX:+HeapDumpOnOutOfMemoryError 表示OOM时导出堆到文件
-XX:HeapDumpPath 导出OOM文件的路径设置(一定得注意如果加了+号下面就会报错)Unexpected +/- setting in VM option 'HeapDumpPath=e:\oom.dump'
-XX:OnOutOfMemoryError 在OOM时,执行一个脚本
import java.util.Vector;
public class TestHeap3{
public static void main(String[] args){
Vector v = new Vector();
for(int i = 0; i < 25; i++){
v.add(new byte[1 * 1024 * 1024]);
}
}
}
java -Xmx20m -Xms5m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=e:\project\JVM\oom.dump TestHeap3
在jdk目录的bin文件夹中的jvisualvm.exe可以打开dump文件
因为byte数组分配了过多的内存,超过最大堆的大小20m,所以发生OOM
总结
永久区分配参数
-XX:PermSize 永久区的初始空间大小
-XX:MaxPermSize 永久区的最大空间
系统启动之后所分配的永久区的空间,接近于-XX:PermSize的值,如果空间不够,可以扩大,但是不能超过MaxPermSize
他们表示,一个系统可以容纳多少个类型
使用CGLIB等库的时候,会产生大量类,这些类,有可能会撑爆永久区导致OOM
如果堆空间没有用完也可能抛出OOM,可能是永久区溢出导致的
3.栈的分配参数
-Xss为栈分配空间
-通常只有几百k
-决定了函数调用的深度
-每个线程上都有独立的空间
-局部变量、参数分配在栈上