前面学习了JVM的内存分布,今天就来验证下。顺便通过测试学习一下JVM的几个参数,不过测试是在idea中,所以先要在idea上设置JVM参数。
一、idea设置全局的JVM参数
一共三步,第一步在菜单栏Help下选择Edit Customer VM Options.......
![883b6fa4c01421e92b1406d40f2292db.png](https://i-blog.csdnimg.cn/blog_migrate/7a12296c8ad01e9a50065f583859d4c9.jpeg)
第二步:可以看到选中后的参数,然后就可以设置常规参数,设置完成后记得重启idea。如下图:
![38ce5d605aaa4278ea16834507c3cd76.png](https://i-blog.csdnimg.cn/blog_migrate/1fc62e8c829d97be0c9ee777ed994e16.jpeg)
第三步:重启后,查看结果,在idea的最右下角会显示总的和使用的,如果没有显示可以设置显示,如下图,在setting中勾选红色框那个选项,点击应用完成。
![32ce2bc131b2fafdf0a2ff1f90358786.png](https://i-blog.csdnimg.cn/blog_migrate/2d854b7ddb956a15de9f76759da6d802.jpeg)
二、针对应用配置JVM参数
第一步:选择Run下面的Edit Configurations....
![4d3312bde7d231d8bb95e59650784de3.png](https://i-blog.csdnimg.cn/blog_migrate/04591755ca20ad9597aa49b874d8732b.jpeg)
第二步:选择想配置的应用,然后在右侧的VM options设置JVM参数。
![fd72932923c9a8e4258a7775ccc7faaf.png](https://i-blog.csdnimg.cn/blog_migrate/dc3c60e8ce4bff23d2969f30f414b5ea.jpeg)
三、JVM内存验证
又要祭出那张内存分布图了,
![5652fa3d0f67e165ee16a34aa83fc2f3.png](https://i-blog.csdnimg.cn/blog_migrate/7a976288b494740fd586d74b3ff9d8fc.jpeg)
如上图,程序计数器不会抛出异常,先不管了,主要是剩下几个的验证。
1、Java堆验证
从之前的学习知道,堆里面放的都是对象和数组,主要是对象。首先是控制堆大小的两个参数-Xms(初始堆大小)、-Xmx(最大对大小)。
测试应用的JVM参数设置:
-Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError
其中+HeapDumpOnOutOfMemoryError参数可以是内存溢出存储内存快照,可用于分析错误。测试结果如下图:
![85cbc0e3ab640d916d1ccc5251ff797b.png](https://i-blog.csdnimg.cn/blog_migrate/c9a07b317661b40357131833b00490ec.jpeg)
红框内就是创建的文件就是内存快照文件,在你的项目的工作目录下,这个是内存溢出前存储的内存情况,可以用专门的工具去分析。这里分享一个在线的分析网站,把Dump文件传上去就可以简单分析下了,网址就不打了,具体如下图:
![78f666765cee4dbfda7f6d75ac8c6459.png](https://i-blog.csdnimg.cn/blog_migrate/5188d489bb1603d4b96763317068c2a9.jpeg)
2、虚拟机栈和本地方法栈验证
HotSpot虚拟机不区分虚拟机栈和本地方法栈(通过java -version可查看是什么虚拟机),所以一起测试了。对应的JVM参数-Xss(每个线程的栈大小)
我们知道栈里面存的是栈帧,栈帧对应的就是线程的方法,所以只要调用的方法多就可以测试出来,最好的办法就是递归了。
测试应用的参数配置:
-Xss128k -XX:+HeapDumpOnOutOfMemoryError
测试结果如下图:
![cfdac245178ab26490d48d7b5e6df38d.png](https://i-blog.csdnimg.cn/blog_migrate/3d2ff9c3906e76523ae22d89f0f15376.jpeg)
虚拟机栈之前说过有两种溢出,线程请求的栈深度大于虚拟机允许的深度,将抛出StackOverflowError异常。如果虚拟机栈可以动态扩展,当扩展的时候没有申请到内存的时候抛出OutOfMemoryError。
这里我们是第一种情况。对于第二种情况,如果在单线程的情况下栈去动态扩展导致内存不足,那么应该也是属于栈不能申请到更多空间,所以是栈溢出,可以明显看出来OutOfMemoryError和StackOverflowError存在包含关系。如果在多个线程下。每个线程的栈都在扩展内存,就会耗光物理内存而造成了OutOfMemoryError。
一个机器的内存是有限的。建立过多线程导致的内存溢出。如果不能减少线程,那么可以通过减少堆和减少栈容量来换取更多线程。这部分可以深入讨论,主要讨论各个模块的关系,和各自的限制。
3、方法区验证
方法区中主要存储被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码。所以我们平时遇到的这类异常一般都是加载的类太多,比如spring这类的框架都会对类进行增强,都会使用到CGLib这类字节码技术,增强的类越多,就需要越大的方法区来保证动态生态的Class可以加载入内存。
jdk8的方法区变成了元空间,使用的是直接内存。所以和jdk7使用的参数不一样具体参数如下:
jdk7:-XX:PermSize=10m -XX:MaxPermSize=10m
jdk8:-XX:MetaspaceSize=10m -XX:MaxMetaspaceSize=10m
结果如下图:
![5b4d482524d9b20023189aebc6655c2b.png](https://i-blog.csdnimg.cn/blog_migrate/3db289af2490cf844171f7570e9a4660.jpeg)
![eb20a8818cc8c83205104690de71fce4.png](https://i-blog.csdnimg.cn/blog_migrate/3ff3bd58b832cb560a795d1dacb8ff58.jpeg)
jdk8的异常更加明显,明确表明是了元空间内存溢出。
4、直接内存验证
直接内存如果不设置和-Xmx(堆最大)一样。可通过参数-XX:MaxDirectMemorySize来设置直接内存。直接内存一般和NIO有关,DirectByteBuffer中通过Unsafe去操作的直接内存,DirectByteBuffer是通过计算知道内存不足抛出的异常,unsafe.allocateMemory()才是去申请内存的方法。测试代码如下图:
测试参数:-Xms20m -XX:MaxDirectMemorySize=10m
![1b15a0bfa3d02915cccdcb3a15d61356.png](https://i-blog.csdnimg.cn/blog_migrate/93c106a112df04029e8a2e72f767a6bb.jpeg)
直接内存的溢出比较简单,dump文件也比较小。如果发现内存溢出也没有明显异常,dump文件比较小,那么就应该想到可以能直接内存溢出,是不是程序中使用了NIO的原因。
四、总结
学习了几个常用JVM参数,总结如下
![0597199b1dfac470274bcb2b658b87a8.png](https://i-blog.csdnimg.cn/blog_migrate/f2bd9d46936662cbade8f11079da5f18.jpeg)
OutOfMemoryError简称OOM,学习了几个基本的内存溢出后,对以后才能有更好的帮助。工作中遇到了内存溢出时,能根据异常信息来判断是那个区域的内存溢出,也知道什么样的代码可能会造成这些区域会出现内存溢出,并且知道一些处理手段。
Java程序员日常学习笔记,如理解有误欢迎各位交流讨论!
![a27407975874b94c91b20d411af3c6b2.png](https://i-blog.csdnimg.cn/blog_migrate/025a5ca1996c6b2b7ff2720f5996a1c9.jpeg)