JVM垃圾收集器(Java Garbage Collection)。本教程均在JDK1.8+HotSpot为例来讲解的.
先来看看Java7的:
![27f72fe0d3d74e3e3238e9dbffb03484.png](https://i-blog.csdnimg.cn/blog_migrate/74eefc8d157b1b7ac89702729957b868.jpeg)
再来看看Jva8的
![eda6bf9d297a6094a84be80e3329fce3.png](https://i-blog.csdnimg.cn/blog_migrate/6ebc3108c67f9a4a9c2df9aaf0382776.jpeg)
从上图中我们可以看出,java8之后换成了元空间。那么怎么证明,堆区是有新生代、永久代、元空间三部分组成的呢?OOM这个错误我们都熟悉,那么怎么手动制造出一个OOM呢?如果16G的物理内存,JVM堆内存能够分到多少G的内存空间呢?我们带着这些疑问来一起学习吧
在Java8中,永久带已经被移除了,被一个称为元空间的区域所取代。元空间的本质和永久带类似。
元空间与永久带之间最大的区别在于:
永久带使用的是JVM的堆内存空间,但是java8以后的元空间并不是虚拟机中的空间,而是使用了本机的物理内存空间的。
因此,默认情况下,元空间大小仅受到本地内存大小的限制。类的元数据放入native memory,字符串常量池和静态类变量存放在java堆区中。这样可以加载多少类的元数据,就不在由MaxPermSize控制了,而是由系统的实际可用空间来控制。
Java默认堆区空间大小是物理内存的六十四分之一(1/64).默认最大堆空间是物理内存的1/4
想要对JVM调优的话,就先要知道自己的家底。默认情况下,当前服务的JVM最大和最小内存是多少呢?怎么查看呢?
我们可以使用Runtime这个类来查看。具体代码如下:
![486460dd669f3f7a5c0a4b45f9060cfa.png](https://i-blog.csdnimg.cn/blog_migrate/083a6a789b2b315f48c8ed332094838b.jpeg)
运行结果:
![efdf042f9a5ebad5a12830347495dbbe.png](https://i-blog.csdnimg.cn/blog_migrate/7e594f4cdd07ee948f4e63794ba16641.jpeg)
来看看凯哥本子上物理内存大小:
![c017b112857ac59b97c088c9917dade5.png](https://i-blog.csdnimg.cn/blog_migrate/8640273aa31c6b4d76214f45591bd469.jpeg)
可以看到是24GB。
从打印的结果,我们看知道,凯哥本子上的JVM最大内存是5.4个G。也就是大约等于物理内存的1/4
JVM最小内存就是:368。大约是物理内存的1/64.
是不是证明了JVM默认堆内存最大值占用物理内存的1/4,最小值占用物理内存的1/64。没有忽悠,没有骗人吧。
![f0cc4b58a4513ec492728aed7118165b.png](https://i-blog.csdnimg.cn/blog_migrate/9e7b0a2104b5e6854d49da46380553ae.jpeg)
看到了吗?totoalMemory方法和maxMemory方法都是native的。在前面,我们讲解JVM体系图的时候,讲解了native关键字修饰的方法,这里就不赘述了。
代码证明堆内存空间就是新生代、老年代、元空间三个区域:
在idea中通过VM options参数来操作
![16eb21ceacac483244796e5288362db9.png](https://i-blog.csdnimg.cn/blog_migrate/29b1a33e31ccd9e4ac48cabeb5d09570.jpeg)
找到需要修改的类,然后在VM options,添加参数。如下图:
![f3549a4a9f3a854180a9d48a26e5c13f.png](https://i-blog.csdnimg.cn/blog_migrate/ab05be25b2a9897e38f440825ef269cb.jpeg)
输入如下参数:-Xms1024m -Xmx1024m -XX:+PrintGCDetails
![e9a654858fa19e19d41b6aa7987a85d9.png](https://i-blog.csdnimg.cn/blog_migrate/c8968849d0d838a66eef63e06a34d794.jpeg)
堆内存调优参数说明:
修改好了之后,重新运行程序,我们看看控制台打印的信息:
![a7a1366cd127a9056f1d4cf71eaf77ea.png](https://i-blog.csdnimg.cn/blog_migrate/4eb5782487f49738d6ac6e6462e70c75.jpeg)
修改后,我们发现堆内存的最大和最小的值是相等的。需要说明一点,在生产环境中,我们最好也把最大和最小值设置一样。这样可以减少空间差距切换从而影响了程序的稳定健壮性。
在上图2部分区域,就是打印出了jvm的详细信息。我们可以明显的看到如下几个数据:
PSYoungGen、ParOldGen、Metaspace这三个区域,正好就是我们之前文章说的,新生代、老年代、元空间这三个区域。这是逻辑上区分的。
在物理上区分是2个,分别是新生代和老年代,怎么证明呢?
![feb7c2eff1fcf61029e31d9f8a8d1310.png](https://i-blog.csdnimg.cn/blog_migrate/d27c04d101fce41aeb88c2830fc8bbe5.jpeg)
还记得我们参数设置的是1024m吧。把新生代和老年代的total相加,是不是就是打印出最大和最小堆内存的值?
再来看看新生代和老年代空间占用比例:305664/699392是不是于等于1/2。
怎么证明新生代是有伊甸园区、from区、to区三部分组成呢?三部分占用比例怎么证明是8/1/1呢?请看下图:
![e000f82a9035a71d30034b5840c82e81.png](https://i-blog.csdnimg.cn/blog_migrate/ebf64c07114ff64bbd82e3490b62a03d.jpeg)
是不是有三个区域。占用空间分别是:26214/43520/43520.是不是就是8/1/1?
现在再回过头,来看看堆内存,是不是更清晰了。
![a714134f7de4de4bd5b11d023b32ee0c.png](https://i-blog.csdnimg.cn/blog_migrate/1b12d0a9a32a52d83e5c0fb96e0af227.jpeg)
通过修改堆参数,模拟出OOM问题
思路:
写个while(true)死循环,通过设置JVM的参数,设置小一点。比如8M,然后执行就会出现OOM。或者new一个字节数组,大于配置的参数就可以。比如设置的堆内存大小是8M,那么byte[] bytes =new byte[10*1024*1024]; //10M的对象。一定会OOM
![5c0996fe4633cdf34544fe76b1511b68.png](https://i-blog.csdnimg.cn/blog_migrate/965a3655a5ca7989a6f5c247f3d71fff.jpeg)
-Xms8m -Xmx8m -XX:+PrintGCDetails
![e7444b059bbe3385091aa5f6309340bc.png](https://i-blog.csdnimg.cn/blog_migrate/12081bbaae07a60db70cb9e081448f27.jpeg)
运行后,查看控制台打印信息.
![710cf2efd85acc7e2b79b3072f457268.png](https://i-blog.csdnimg.cn/blog_migrate/4c4cc9150fbce03c106b087d60a1c07e.jpeg)
是不是看到了熟悉的
[Full GC (Allocation Failure) Exception in thread "main" java.lang.OutOfMemoryError: Java heap space。
下一篇文章预告:GC收集日志信息分析。欢迎大家和凯哥(凯哥java:kaigejava)一起继续学习。