目录
jvm的内存模型中,运行时数据区分为哪几个部分?
JVM如何判断一个对象所占内存是可以被回收的?
把一系列称为 “GC Roots” 的引用(很多博客说GC Root是对象,严格来讲是对象的引用,并不是对象本身,包括下面列举的四种,也是引用。但是大家都说成对象,也不纠结这些了。)作为起始点,从这些起始点向下搜索,搜索走过的路径就是引用链,当一个对象到 GC Roots 没有任何引用链相连,也就是从 GC Roots 到这个对象不可达时,这个对象可以被回收。
GCRoot包括:
- 虚拟机栈(栈帧中的本地变量表)中的引用
- 本地方法栈中 JNI(即一般说的 Native 方法)引用
- 方法区中类静态属性的引用
- 方法区中常量引用
通俗的讲:
1. 虚拟机栈(栈帧中的本地变量表)中引用,是指main函数内新建的局部变量。
2. 本地方法栈中 JNI(即一般说的 Native 方法)引用,JNI核心方法内新建的局部变量。
3. 方法区中类静态属性引用的对象,是指所有的类变量(类的static成员变量)引用。
4. 方法区中常量引用的对象,是指所有的常量(static final)引用。
JVM中如何判断对象可以被回收?
GC Roots 是什么?哪些对象可以作为 GC Root?看完秒懂!
什么是年轻代?
- 堆内存分为年轻代(Young Generation)、老年代(Old Generation),比例为1:2。年轻代又分为Eden和Survivor区。Survivor区由FromSpace和ToSpace组成。Eden区占大容量,Survivor两个区占小容量,默认比例是8:1:1。。
- 永久代(Permanent Generation)是方法区的实现,存储程序运行时长期存活的对象,比如类的元数据、方法、常量、属性等。
方法区和永久带的联系是什么?
方法区和永久代的关系很像Java中接口和类的关系,类实现了接口,而永久代就是HotSpot虚拟机对虚拟机规范中方法区的一种实现方式。
Java方法区和永久代
为什么要分年轻代和老年代?
- 新生代(Young Gen):年轻代主要存放新创建的对象,内存大小相对会比较小,垃圾回收会比较频繁。年轻代分成1个Eden Space和2个Suvivor Space(from 和to)。
- 老年代(Tenured Gen):老年代主要存放JVM认为生命周期比较长的对象(经过15次的Young Gen的垃圾回收后仍然存在),这些对象内存大小相对会比较大,还有大对象,老年代的垃圾回收也相对没有那么频繁。
为什么新生代和老年代要采用不同的回收算法?
- 在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就许选择“标记-复制”算法,只需要复制少量存活对象,就可以完成收集。
- 老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用“标记–清除”、“标记整理”算法来进行回收。如果在老年代采用标记复制算法,需要复制的对象很多,效率不高。
为什么新生代要分出Survivor区?
- Survivor的存在意义,就是减少被送到老年代的对象,进而减少Full GC的发生。Survivor的预筛选保证,除了大对象(超过Survivor一半)和累加超过Survivor一半的对象之外,其他对象如果在新生代中经历16次Minor GC还能存活,才会被送到老年代。
详见 为什么新生代内存需要有两个Survivor区?
为什么有两个Survivor区?
-
两个Survivor区可以减少复制次数,提高效率。
- 年轻代采用复制算法,如果年轻代只有Eden区和from区,恰好Eden区和From区都存在需要回收的垃圾对象,Eden区的存活对象可以复制到from区对象的后面,然后把Eden区清空。但是From区有垃圾对象也有存活对象,from区的这些对象又往哪里复制?
- 如果往老年代复制,老年代也会快速装满,引发full GC,性能降低。
- 如果往清空后的Eden区复制, 比如:Eden区ABCDEF对象中,AB存活,From区XYZ对象,X存活。先把AB复制到From区,把Eden区清空,然后再把ABX复制到Eden区,From区清空,From区本身比较小,能装的对象不多,还需要把Eden区的ABX再复制回来。这样做也不是不行,只是相对于from+to机制来说,AB对象多了两次次复制,效率明显低了。再from+to机制下,Eden和from区的存活对象直接复制到to区,然后清空Eden和from区,并且交换from和to 。
- 年轻代采用复制算法,如果年轻代只有Eden区和from区,恰好Eden区和From区都存在需要回收的垃圾对象,Eden区的存活对象可以复制到from区对象的后面,然后把Eden区清空。但是From区有垃圾对象也有存活对象,from区的这些对象又往哪里复制?
-
另外一篇文章:为什么新生代内存需要有两个Survivor区,给出的观点是两个Survivor区是为了解决碎片化问题,这个观点本人不大认可,但是文章还是值得读的。 因为复制算法本身已经解决了碎片问题,上面的思路2就是例证,只是效率低。所以还是这样做的效率更高。
聊聊JVM的年轻代
为什么新生代内存需要有两个Survivor区
为什么新生代内存需要有两个Survivor区?(腾讯云)
MinorGC和Full GC的区别是什么?
-
MinorGC也称作Young GC,只对Young Gen进行回收。Eden区内存不足时会触发Minor GC。
-
Full GC对年轻代、老年代都进行内存回收。Full GC的触发条件是:
- 调用System.gc()只是建议虚拟机执行 Full GC,但是虚拟机不一定真正去执行。
- 未指定老年代和新生代大小,堆伸缩时会产生fullgc。
- 老年代空间不足。
- JDK 1.7 及以前的(永久代)空间满。
- 空间分配担保失败。
Full GC 和 Minor GC,傻傻分不清楚
一文搞懂Y-GC和Full GC的触发条件
什么时候会触发Full GC
Java堆内存的新生代Survivor区“To”被填满了,to区中的有的对象年龄还没被复制15次,也会被移动到年老代中吗?
-
虚拟机并不是永远地要求对象的年龄必须达到了MaxTenuringThreshold才能晋升老年代,如果在Survivor空间中具有相同年龄N的所有对象,如果它们内存大小的总和大于Survivor空间的一半,年龄大于或等于m的对象就可以直接进入老年代,无须等到MaxTenuringThreshold中要求的年龄。
例如:下面的场景MaxTenuringThreshold为15
年龄1的对象占用了33%
年龄2的对象占用33%
年龄3的对象占用34%。年龄1的占用了33%,年龄2的占用了33%,累加和超过默认的TargetSurvivorRatio(50%),年龄2和年龄3的对象都要晋升。
方法区可以GC吗?
根据Java虚拟机规范的规定,方法区无法满足内存分配需求时,也会抛出OutOfMemoryError异常,虽然规范规定虚拟机可以不实现垃圾收集,因为和堆的垃圾回收效率相比,方法区的回收效率实在太低,但是此部分内存区域也是可以被回收的。
方法区的垃圾回收主要有两种,分别是对废弃常量的回收(常量池的回收)和对无用类的回收(类的卸载)。
JVM:方法区可以GC吗?
垃圾回收会造成,界面卡顿。
详解Java的垃圾回收机制(GC)
Java虚拟机垃圾回收(三) 7种垃圾收集器
Java虚拟机(JVM)你只要看这一篇就够了!
300赞:浅析JAVA的垃圾回收机制(GC)