摘要:第二步分析第一个循环即表示产生的对象,后面规律相同和会直接放入区。因为优先放区,而且够放,此时为两者表示已用剩余。此值一般设置与相同以避免每次垃圾回收完后重新分配内存设置年轻代大小为。
一、概述
闲来有空翻翻书,捡捡一些基础点,就当静下心多写字。
Java基础的东西无论怎么样都会想到JVM,而提JVM必然想到最常见的一些点:字节码加载,类初始化,方法执行,对象内存分配和回收,线程和锁机制等等。归纳整理的时候,怎么可以少了它们。不过,我打算换个方式,不写太多概念(网上一搜一把的),想从一些代码、例子、题目或者疑问等方面来写写。
JVM内存管理需要理解的点:内存空间的划分、内存分配和内存回收。
内存空间:认识方法区、堆区、本地方法栈等几个空间,了解堆的分代管理。
内存分配:在Java中这块比较容易,就是栈和堆,而创建对象都在堆区。
内存回收:实战上最主要是关注什么时候会触发回收(GC)和回收对性能的影响,学习上可以了解不同回收起和回收算法。
验证和测试需要的点:常见的启动参数、GUI类工具。
常见的启动参数:-Xms -Xmx -Xmn 等等
GUI类工具:JProfiler(推荐)、JVisualVM、MAT、JMap、JHat。
三、一个GC题目1)当用-Xms30m -Xmx30m -Xmn10m -XX:+UseParallelGC 执行上面的代码时会执行几次Minor GC和几次Full GC呢?
2)分别说明你的结果是如何推出来的?
public static void main(String[] args) throws Exception{ List
先思考下,别忙着往下看,万一我的分析并不对呢!!
先思考下,别忙着往下看,万一我的分析并不对呢!!
先思考下,别忙着往下看,万一我的分析并不对呢!!
第一步分析启动参数:
首先,看到"UseParallelGC"参数,表示这里采用的是“Parallel Scavenge+Serial Old”。那么,从回收器的类型,可以知道堆的新生代是基于复制算法的gc(即内存模型有from和to区),堆的老年代则基于标记-整理算法。
其次,看到"-Xms30m -Xmx30m"参数,表示堆区最大为30M,且不会动态扩展。
最后,再看到"-Xmn10m"参数,表示新生代区为10m,而且采用默认的7.5:1,即Eden7.5M(7680k),而from和to区各为1.25M。
第二步分析第一个循环
i0(即表示i=0产生的对象,后面规律相同)和i1 会直接放入Eden区。因为优先放Eden区,而且够放,此时Eden为 6m/7.5m.(两者表示 已用/剩余 。此处的6m为近似值,其他jvm对象之类占用内存的,不细讨论)。
i2来了,要放入Eden区,发现空间不够。触发MinorGC。然后把i2放到Eden。
结果将i0~i1直接诶转入老年代 ,原因是对象3m太大放不了From区(前面提到才1.25m)。
此时Eden区 3m/7.5m 老年代6m/20m
gc的log --> [PSYoungGen: 6451K->272K(8960K)] 6451K->6416K(29440K)
i3来了,直接继续放入Eden区。 此时Eden区 6m/7.5m 老年代6m/20m
i4来了,跟i2一样的情况,发现Eden不够放了。再次触发MinorGC。然后把i4放到Eden。
结果将i2~i3直接诶转入老年代,去陪i0和i1了。
此时Eden区 3m/7.5m 老年代12m/20m
gc的log --> [PSYoungGen: 6650K->256K(8960K)] 12794K->12544K(29440K)
i5来了,继续放入Eden区,此时加上前面i4,Eden区 6m/7.5m 老年代12m/20m
i6来了,跟前面i2、i4情况一样,触发了一次MinorGC。然后把i6放到Eden。
结果: i6 在Eden,i0~i5 6个在老年代。 此时Eden区 3m/7.5m 老年代18m/20m
gc的log -->[PSYoungGen: 6453K->224K(8960K)] 18741K->18656K(29440K)
这里还多了一次FullGC。
gc的log -->[PSYoungGen: 240K->0K(8960K)] [PSOldGen: 18432K->18593K(20480K)]
暂时未能完全分析明白这点,但gc日志来猜测,应该是标记-整理起作用了,为了整理出连续的空间吧。
第三步: 由于执行了caches.clear(); 等于宣告前面的i0~i6的7个对象都不可用了(即GC Roots不可达)
但还由于各区都有足够大的空间,只要程序运行未达到"GC安全点"是不会触发GC的。
第四步:
j0来了,它会继续放到Eden区,此时陪着还没回收的i6。
此时Eden区 6m/7.5m 老年代18m/20m
j1来了,这时候内存管理会发现新生代和老年代都不够空间申请了,即触发FullGC。
结果:从老年代把不可用的i0~i5全部干掉,再把j0移入老年代,再把新生代中的i6干掉,然后j1放入新生代。
此时Eden区 3m/7.5m 老年代3m/20m
gc的log -->[PSYoungGen: 6178K->0K(8960K)] [PSOldGen: 18593K->3233K(20480K)]
运行上面的程序,参数为: -Xms30m -Xmx30m -Xmn10m -XX:+UseParallelGC -XX:+PrintGCDetails
加上-XX:+PrintGCDetails,这样可以打印GC的log情况来验证前面的分析。
ps:不同的虚拟机版本打印的输出多少会有差异,以下log,是我在sum的1.6.0_43版本打印出来的,另外删减掉一些不关心的输出。
[GC [PSYoungGen: 6451K->320K(8960K)] [GC [PSYoungGen: 6698K->240K(8960K)] [GC [PSYoungGen: 6437K->240K(8960K)] [Full GC [PSYoungGen: 240K->0K(8960K)] [PSOldGen: 18432K->18594K(20480K)] 18672K->18594K(29440K) [Full GC [PSYoungGen: 6178K->0K(8960K)] [PSOldGen: 18594K->3234K(20480K)] 24773K->3234K(29440K) Heap PSYoungGen total 8960K, used 3248K >eden space 7680K, 42% used >from space 1280K, 0% used >to space 1280K, 0% used PSOldGen total 20480K, used 3234K PSPermGen total 21248K, used 3043K六、最后补充涉及到的知识点 6.1 关于堆区分代管理
新生代GC(Minor GC):主要是发生在新生代的收集动作,据说IBM做过调查,绝大多数对象都是朝生夕死,所以MinorGC非常频繁,速度也比较快。
老年代GC(Full GC):是指发生在老年代的收集动作,但是通常也会对年轻代进行垃圾收集。
-Xmx 设置JVM最大可用内存为30M。
-Xms 设置JVM扩展内存为30M。(此值一般设置与-Xmx相同,以避免每次垃圾回收完后JVM重新分配内存)
-Xmn:设置年轻代大小为10m。 (整个堆大小=年轻代大小 + 年老代大小 持久代大小 : 持久代PermGen是非堆的,可以通过jconsole查看)
-Xss128k:(设置每个线程的堆栈大小)
持久代,是通过PermSize和MaxPermSize
-XX:+UseParallelGC:选择垃圾收集器为并行收集器。
串行处理器: 适用数据量比较小(100M左右);单处理器下并且对响应时间无要求的应用。 缺点:只能用于小型应用
并行处理器: 适用“对吞吐量有高要求”,多CPU、对应用响应时间无要求的中、大型应用。举例:后台处理、科学计算。 缺点:应用响应时间可能较长
并发处理器: 适用“对响应时间有高要求”,多CPU、对应用响应时间有较高要求的中、大型应用。举例:Web服务器/应用服务器、电信交换、集成开发环境。
GC Roots不可达:GC回收过程判断对象是否可以回收的一种方式,叫可达性测试!
打印gc日志:启动程序时加vm参数-XX:+PrintGCDetails。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/65587.html
摘要:而字节码运行在之上,所以不用关心字节码是在哪个操作系统编译的,只要符合规范,那么,这个字节码文件就是可运行的。好处防止内存中出现多份同样的字节码安全性角度特别说明类加载器在成功加载某个类之后,会把得到的类的实例缓存起来。 前言 只有光头才能变强 JVM在准备面试的时候就有看了,一直没时间写笔记。现在到了一家公司实习,闲的时候就写写,刷刷JVM博客,刷刷电子书。 学习JVM的目的也很简单...
摘要:在这种消耗很高的状态下,应用程序所有的线程都会挂起,暂停一切正常的工作,等待垃圾回收的完成。但是,因为线程切换和上下文转换的消耗,会使得垃圾回收的总体成本上升,造成系统吞吐量的下降。 Java 垃圾回收(GC) 泛读 文章地址: https://segmentfault.com/a/1190000008922319 0. 序言 带着问题去看待 垃圾回收(GC) 会比较好,一般来说主要的...
摘要:当一个实例被创建的时候,它最初被存放在堆内存空间的年轻代的区中。老年代或者永久代是堆内存的第二个逻辑部分。在垃圾回收过程中扫描属于部分的堆内存。一旦实例从堆内存中删除了,它们原来的位置将空出来给以后分配实例使用。 本文非原创,翻译自How Java Garbage Collection Works?在Java中为对象分配和释放内存空间都是由垃圾回收线程自动执行完成的。和C语言不一样的是...
摘要:这个算法看似不错而且简单,不过存在这一个致命伤当两个对象互相引用的时候,就永远不会被回收于是引用计数算法就永远回收不了这两个对象,下面介绍另一种算法。 前言 如果要问Java与其他编程语言最大的不同是什么,我第一个想到的一定就是Java所运行的JVM所自带的自动垃圾回收机制,以下是我学习JVM垃圾回收机制整理的笔记,希望能对读者有一些帮助。 哪些内存需要回收?what? ...
阅读 3434·2021-10-13 09:39
阅读 1399·2021-10-08 10:05
阅读 2224·2021-09-26 09:56
阅读 2243·2021-09-03 10:28
阅读 2638·2019-08-29 18:37
阅读 2000·2019-08-29 17:07
阅读 565·2019-08-29 16:23
阅读 2116·2019-08-29 11:24