在上一次【https://www.cnblogs.com/webor2006/p/10990736.html】中实验了一个从新生代到老年代竞升的情况,得出一个这样的结果,回忆下:
![](https://i-blog.csdnimg.cn/blog_migrate/b8814e52a0813cd33cca1c9bd7717dd0.png)
其实对于JVM来说,它提供了相应的一些参数来指定对象达到了多少的时候直接就可以在老年代分配而不是在新生代来分配,新生代是喜欢小的而且生命短暂的对象,所以下面来做实验,先新建一个类,示例代码基本上跟上一次类似:
![](https://i-blog.csdnimg.cn/blog_migrate/32baab1a3d7067834370d68bc2f11932.png)
接下来需要介绍一个打印JVM启动的默认参数的命令,如下:
![](https://i-blog.csdnimg.cn/blog_migrate/1fca3fba0d502ba62488b2c70c98a3e6.png)
下面具体来看一下这些默认JVM启动参数的含义:
![](https://i-blog.csdnimg.cn/blog_migrate/c34d46b0d196118c61f585d293a43f85.png)
它其实等价于我们在上一个程序添加的JVM的这个参数:
![](https://i-blog.csdnimg.cn/blog_migrate/8d19e7db4304ce77f4fb1cdbbd3bd40a.png)
表示堆的初始大小。
![](https://i-blog.csdnimg.cn/blog_migrate/323c2258c9e3ce55bb6e31fc591758fd.png)
其实等价于:
![](https://i-blog.csdnimg.cn/blog_migrate/af6b0d2da4a7235d5a4d866b5c423585.png)
表示堆的最大大小。
![](https://i-blog.csdnimg.cn/blog_migrate/b0c731b26e699fdf78c0d2d7d047ebdb.png)
![](https://i-blog.csdnimg.cn/blog_migrate/3d91d2345002fac5b882e68c9410d2cf.png)
![](https://i-blog.csdnimg.cn/blog_migrate/b65a1230f1bb61724ee4f5d94670ffca.png)
啥意思?这里做个了解:因为32位的寻址空间跟64位的寻址空间显然是不一样的,64位的寻址空间要更大一些,但是如果从32位牵移到64位的虚拟机上,由于寻址空间变大,所以相应的指针也会变大,也就是说JVM在启动时占用的内存空间也更多,当加上此JVM启动参数之后则会针对特定【具体哪几类这里就不过多说明,可以网上搜寻,这里仅做了解】的指针进行一个压缩,使得JVM占用的内存空间更小。
![](https://i-blog.csdnimg.cn/blog_migrate/680f0ec4ca5a7adf6fbc60d8b191d610.png)
这也就是为啥我们在上一次的程序中看到的新生代和老年代的垃圾收集器默认就是:
![](https://i-blog.csdnimg.cn/blog_migrate/d04328d0d4dfcc3f9237beb681791494.png)
原因就在于JVM默认指定的垃圾收集器是指定的ParallelGC。有了这样的一个背景知识之后,接下来咱们给这个新的程序增加一些JVM参数,有一些参数跟上一次设置的差不多,但也有些是新的,先将上一次的参数直接拷贝过来:
![](https://i-blog.csdnimg.cn/blog_migrate/70fddcd04fb3d42de0e05e0a39b94bea.png)
紧接着再增加一些新的参数,如下:
![](https://i-blog.csdnimg.cn/blog_migrate/56f2519be305d17f9b1fe87b3256609c.png)
其实在之前的理论学习中也有说明,回忆一下:
![](https://i-blog.csdnimg.cn/blog_migrate/6a7f3c77ac936aa545a1981fde5843f8.png)
所以,咱们改一下程序,直接申请一个大于这个值的内存大小,如下:
![](https://i-blog.csdnimg.cn/blog_migrate/30ef983afd64d12ff469690b627bd1c2.png)
这是为啥呢?其实还得指定另外一个参数,我们指定的“-XX:PretenureSizeThreshold=4194304”才会生效,这里需要指定串行收集器才行,啥叫串行收集器,回顾下之前的理论:
![](https://i-blog.csdnimg.cn/blog_migrate/8b12470bc42764e3e7425ad959ec226b.png)
而对应的JVM参数为:
![](https://i-blog.csdnimg.cn/blog_migrate/3ee80a435f62e29f732a77623760e319.png)
所以咱们加上该JVM参数:
![](https://i-blog.csdnimg.cn/blog_migrate/cba075485ff6ca76e38e4e1f85c376b7.png)
再运行,此时看到的结果会大不一样:
![](https://i-blog.csdnimg.cn/blog_migrate/bab0b5f99b91e2ffa9a9c77f690d91cb.png)
其中关键处可以发现,我们申请的5M空间直接就进入到了老年代了,如下:
![](https://i-blog.csdnimg.cn/blog_migrate/44c01162b1bc990b16bc954f1105e8c2.png)
所以通过上面的方式就能指定老年代的阈值,好,咱们还是回到ParallelGC,也就是将最后一个JVM参数删掉,来回顾下“如果在新生代中已经无法容纳下一个新创建的对象的话,该新对象会直接在老年代来诞生”的情况:
![](https://i-blog.csdnimg.cn/blog_migrate/6df536fc8f7ee03d6da112a6412b5480.png)
然后咱们直接来申请8M的空间,这样在Eden新生代中肯定容纳不下:
![](https://i-blog.csdnimg.cn/blog_migrate/2d77c1ad25b856c251408bf1cada5315.png)
那如果继续扩大申请的内存又会是啥输出呢,这里改到10M:
![](https://i-blog.csdnimg.cn/blog_migrate/8ce3ea41d1482fe3e227768f237ccbe7.png)
/Library/Java/JavaVirtualMachines/jdk1.8.0_92.jdk/Contents/Home/bin/java -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:PretenureSizeThreshold=4194304 -Dfile.encoding=UTF-8 -classpath /Library/Java/JavaVirtualMachines/jdk1.8.0_92.jdk/Contents/Home/jre/lib/charsets.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_92.jdk/Contents/Home/jre/lib/deploy.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_92.jdk/Contents/Home/jre/lib/ext/cldrdata.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_92.jdk/Contents/Home/jre/lib/ext/dnsns.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_92.jdk/Contents/Home/jre/lib/ext/jaccess.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_92.jdk/Contents/Home/jre/lib/ext/jfxrt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_92.jdk/Contents/Home/jre/lib/ext/localedata.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_92.jdk/Contents/Home/jre/lib/ext/nashorn.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_92.jdk/Contents/Home/jre/lib/ext/sunec.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_92.jdk/Contents/Home/jre/lib/ext/sunjce_provider.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_92.jdk/Contents/Home/jre/lib/ext/sunpkcs11.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_92.jdk/Contents/Home/jre/lib/ext/zipfs.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_92.jdk/Contents/Home/jre/lib/javaws.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_92.jdk/Contents/Home/jre/lib/jce.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_92.jdk/Contents/Home/jre/lib/jfr.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_92.jdk/Contents/Home/jre/lib/jfxswt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_92.jdk/Contents/Home/jre/lib/jsse.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_92.jdk/Contents/Home/jre/lib/management-agent.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_92.jdk/Contents/Home/jre/lib/plugin.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_92.jdk/Contents/Home/jre/lib/resources.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_92.jdk/Contents/Home/jre/lib/rt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_92.jdk/Contents/Home/lib/ant-javafx.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_92.jdk/Contents/Home/lib/dt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_92.jdk/Contents/Home/lib/javafx-mx.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_92.jdk/Contents/Home/lib/jconsole.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_92.jdk/Contents/Home/lib/packager.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_92.jdk/Contents/Home/lib/sa-jdi.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_92.jdk/Contents/Home/lib/tools.jar:/Users/xiongwei/Documents/workspace/IntelliJSpace/jvm_lectue/out/production/classes:/Users/xiongwei/.gradle/caches/modules-2/files-2.1/mysql/mysql-connector-java/5.1.34/46deba4adbdb4967367b013cbc67b7f7373da60a/mysql-connector-java-5.1.34.jar:/Users/xiongwei/.gradle/caches/modules-2/files-2.1/cglib/cglib/3.2.0/bced5c83ed985c080a24dc5a42b0ca631556f413/cglib-3.2.0.jar:/Users/xiongwei/.gradle/caches/modules-2/files-2.1/org.ow2.asm/asm/5.0.3/dcc2193db20e19e1feca8b1240dbbc4e190824fa/asm-5.0.3.jar:/Users/xiongwei/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant/1.9.4/6d473e8653d952045f550f4ef225a9591b79094a/ant-1.9.4.jar:/Users/xiongwei/.gradle/caches/modules-2/files-2.1/org.apache.ant/ant-launcher/1.9.4/334b62cb4be0432769679e8b94e83f8fd5ed395c/ant-launcher-1.9.4.jar com.jvm.gc.MyTest2
[GC (Allocation Failure) [PSYoungGen: 1023K->432K(9216K)] 1023K->440K(19456K), 0.0009855 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 432K->416K(9216K)] 440K->424K(19456K), 0.0007116 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (Allocation Failure) [PSYoungGen: 416K->0K(9216K)] [ParOldGen: 8K->343K(10240K)] 424K->343K(19456K), [Metaspace: 2649K->2649K(1056768K)], 0.0048980 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 0K->0K(9216K)] 343K->343K(19456K), 0.0004381 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (Allocation Failure) [PSYoungGen: 0K->0K(9216K)] [ParOldGen: 343K->331K(10240K)] 343K->331K(19456K), [Metaspace: 2649K->2649K(1056768K)], 0.0068370 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at com.jvm.gc.MyTest2.main(MyTest2.java:6)
Heap
PSYoungGen total 9216K, used 246K [0x00000007bf600000, 0x00000007c0000000, 0x00000007c0000000)
eden space 8192K, 3% used [0x00000007bf600000,0x00000007bf63d890,0x00000007bfe00000)
from space 1024K, 0% used [0x00000007bfe00000,0x00000007bfe00000,0x00000007bff00000)
to space 1024K, 0% used [0x00000007bff00000,0x00000007bff00000,0x00000007c0000000)
ParOldGen total 10240K, used 331K [0x00000007bec00000, 0x00000007bf600000, 0x00000007bf600000)
object space 10240K, 3% used [0x00000007bec00000,0x00000007bec52d00,0x00000007bf600000)
Metaspace used 2681K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 290K, capacity 386K, committed 512K, reserved 1048576K
Process finished with exit code 1
可见直接抛内存溢出异常了,而且此时日志的输出信息也比较多,稍加分析:
![](https://i-blog.csdnimg.cn/blog_migrate/6c92cf7bbfcb862e68a2e0fd3daabea2.png)
而且有个小细节,此时FULL GC的原因变为了:
![](https://i-blog.csdnimg.cn/blog_migrate/f1545785ab638727a39c5625480fe678.png)
而非之前咱们看到的:
![](https://i-blog.csdnimg.cn/blog_migrate/3d00420d6f4d9a8251fc98bdb2efe271.png)
接下来咱们准备用之前学习的可视化监测工作来瞅一下GC的情况,先还是将串行的参数加上,并程序进行还原:
![](https://i-blog.csdnimg.cn/blog_migrate/893e4de435bc77bd404afa3c1a7ebc10.png)
为了能监视,我们将程序进行一下休眠:
![](https://i-blog.csdnimg.cn/blog_migrate/ac80569f77129eb28d0b2c89138a5e3c.png)
首先咱们用jvisualvm来查看一下:
![](https://i-blog.csdnimg.cn/blog_migrate/f1e4f7497ce6ff9d59a80e208dac4351.png)
![](https://i-blog.csdnimg.cn/blog_migrate/d7e4ba66e885c965504a9bb7d1fa31fd.png)
![](https://i-blog.csdnimg.cn/blog_migrate/323301e7be96e4e18c57bba5325bd787.png)
![](https://i-blog.csdnimg.cn/blog_migrate/92c0425e92ed24279809f2894b49785f.png)
此时会看到会有GC出现,是因为可视化的工具会attach到我们的进程上会造成GC动作,咱们看一下堆的情况:
![](https://i-blog.csdnimg.cn/blog_migrate/fd69bf8df59f06c94bd2ff8614e2b32b.png)
![](https://i-blog.csdnimg.cn/blog_migrate/84bed5020728b23e118f1cdda7431849.png)
另外咱们还可以通过该工具主动让JVM进行GC,如下:
![](https://i-blog.csdnimg.cn/blog_migrate/08c812466ba3b88550e5051273b10c48.png)
比如我连续点3次,则会出现三次FULL GC:
![](https://i-blog.csdnimg.cn/blog_migrate/e9190d728d41f98c7e10898221584c8d.png)
此时的GULL GC的原因就变为System.gc()了,我们知道对于“System.gc()”它被调用之后JVM并不一定会立马执行,但是它的意义还是挺大的,为啥,因为对于GC点的出现正常只支在我们创建对象的那一刻才会引起,有了这个命令就让我们手动去在想要GC的时候尽可能的发生作用。
好,接下来咱们再换一个可视化的工具,换成jmc:
![](https://i-blog.csdnimg.cn/blog_migrate/c58ada512bcb61e21ae36b70e0bd867d.png)
![](https://i-blog.csdnimg.cn/blog_migrate/58f2573d28108bdc332657c6a2a14483.png)
![](https://i-blog.csdnimg.cn/blog_migrate/60abe692170729d55329f0805e334e08.png)
其中再看一下诊断命令:
![](https://i-blog.csdnimg.cn/blog_migrate/16e0bbaf1f3f9f9dea26eff076eecf94.png)
其实它就是来自于jcmd命令中,如我们也可以用这个命令来查看进程JVM的参数信息,如下:
![](https://i-blog.csdnimg.cn/blog_migrate/31421867fce4830611c2266b93cba34f.png)
对应的在诊段命令中执行一下也可以瞅见:
![](https://i-blog.csdnimg.cn/blog_migrate/af7be89a483cf8603c21596cac720fdb.png)
另外该工具中的飞行记录器也可以瞅一下内存情况:
![](https://i-blog.csdnimg.cn/blog_migrate/be767c7c0e6afe9b06ac2e76cde194b5.png)
当然具体里面有何用还得是在未来有需要再慢慢熟悉。