大佬教程收集整理的这篇文章主要介绍了深入理解JVM虚拟机读书笔记——垃圾回收算法,大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。
注:本文参考自周志明老师的著作《深入理解Java虚拟机(第3版)》c;相关电子书可以关注WX公众号c;回复 001 获取。
JVM 中判断对象是否已经死亡的算法主要有 2 种:引用计数法、可达性分析法。
+1
c;如果该对象被引用2次则其引用计数为2c;依次类推。-1
c;当该对象的引用计数变为0时c;则表示该对象没用被其他变量所引用c;这时候该对象就可以被作为垃圾进行回收。引用计数法弊端:循环引用时c;两个对象的引用计数都为1c;导致两个对象都无法被释放回收。最终就会造成内存泄漏!
可达性分析算法就是JVM中判断对象是否是垃圾的算法:该算法首先要确定GC Root(根对象c;就是肯定不会被当成垃圾回收的对象)。
在垃圾回收之前c;JVM会先对堆中的所有对象进行扫描c;判断每一个对象是否能被GC Root直接或者间接的引用c;如果能被根对象直接或间接引用则表示该对象不能被垃圾回收c;反之则表示该对象可以被回收:
上图实心线表示强引用:比如c;new
一个对象Mc;将对象M通过=
(赋值运算符)c;赋值给某个变量mc;则变量m就强引用了对象M。
强引用的特点:只要沿着GC Root的引用链能够找到该对象c;就不会被垃圾回收;只有当GC Root都不引用该对象时c;才会回收强引用对象。
上图中宽虚线所表示的就是软引用:
软引用的特点:当GC Root指向软引用对象时c;若内存不足c;则会回收软引用所引用的对象。
软引用的使用:
public class Demo1 {
public static void @H_226_142@main(String[] args) {
final int _4M = 4*1024*1024;
// 软引用对象内部包装new byte[_4M]对象
SoftReference<byte[]> ref= new SoftReference<>(new byte[_4M]);
// 这时List 跟 SoftReference之间是强引用c;SoftReference 跟 byte[] 之间是软引用
List 跟 <SoftReference<byte[]>> list = new ArrayList<>();
}
}
如果在垃圾回收时发现内存不足c;在回收软引用所指向的对象时c;软引用本身不会被清理。
如果想要清理软引用c;需要使用引用队列:
public class Demo04 {
final static int _4M = 4 * 1024 * 1024;
public static void @H_226_142@main(String[] args) {
// List和SoftReference是强引用c;而SoftReference和byte数组则是软引用
List<SoftReference<byte[]>> list = new ArrayList<>();
// 引用队列c;用于移除引用为空的软引用对象
ReferenceQueue<byte[]> queue = new ReferenceQueue<>();
for (int i = 0; i < 5; i++) {
// 关联引用队列c;当软引用所关联的 byte[]被回收时c;软引用自己会假如到queue中去
SoftReference<byte[]> ref = new SoftReference<>(new byte[_4M], queue);
System.out.println(ref.get());
list.add(ref);
System.out.println(list.size());
}
// 遍历c;从引用队列中获取无用的软引用对象c;并移除
Reference<? extends byte[]> poll = queue.poll();
while (poll != null) {
// 引用队列不为空c;则从集合中移除该元素
list.remove(poll);
// 移动到引用队列中的下一个元素
poll = queue.poll();
}
System.out.println("==========================");
for (SoftReference<byte[]> reference : list) {
System.out.println(reference.get());
}
}
}
**大概思路为:**查看引用队列中有无软引用c;如果有c;则将该软引用从存放它的集合中移除(这里为一个list集合)。
只有当弱引用引用该对象c;在垃圾回收时c;无论内存是否充足c;都会回收弱引用所引用的对象。
弱引用的使用和软引用类似c;只是将 SoftReference 换为了 WeakReference。
当引用的对象ByteBuffer被垃圾回收以后c;虚引用对象Cleaner就会被放入引用队列中:
然后调用Cleaner的clean
方法(Unsafe.freeMemory()
)来释放直接内存:
clean
方法来释放直接内存。clean
方法来释放直接内存。所有的类都继承自Object类c;Object类有一个finalize()
方法。当某个对象不再被其他的对象所引用时c;会先将终结器引用对象放入引用队列中c;然后根据终结器引用对象找到它所引用的对象c;然后调用该对象的finalize()
方法。调用以后c;该对象就可以被垃圾回收了。
finalize()
方法。调用以后c;该对象就可以被垃圾回收了。方法区的垃圾收集主要回收两部分内容:废弃的常量和不再使用的类型。举个常量池中字面量回收的例子c;假如一个字符串“java”曾经进入常量池中c;但是当前系统又没有任何一个字符串对象的值是“java”c;换句话说c;已经没有任何字符串对象引用常量池中的“java”常量c;且虚拟机中也没有其他地方引用这个字面量。如果在这时发生内存回收c;而且垃圾收集器判断确有必要的话c;这个“java”常量就将会被系统清理出常量池。常量池中其他类(接口)、方法、字段的符号引用也与此类似。
判定一个常量是否“废弃”需要同时满足下面三个条件:
java.lang.Class
对象没有在任何地方被引用c;无法在任何地方通过反射访问该类的方法。在Java堆划分出不同的区域之后c;垃圾收集器才可以每次只回收其中某一个或者某些部分的区域——因而才有了“@H_236_7@minor GC”“@H_236_7@major GC”“Full GC”这样的回收类型的划分;也才能够针对不同的区域安排与里面存储对象存亡特征相匹配的垃圾收集算法——因而发展出了“标记-复制算法”“标记-清除算法”“标记-整理算法”等针对性的垃圾收集算法。
四种GC概念的介绍:
■ 新生代收集(Minor GC/Young GC):指目标只是新生代的垃圾收集。
■ 老年代收集(Major GC/Old GC):指目标只是老年代的垃圾收集。目前只有CMS收集器会有单独收集老年代的行为。
■ 混合收集(Mixed GC):指目标是收集整个新生代以及部分老年代的垃圾收集。目前只有G1收集器会有这种行为。
■ 整堆收集(Full GC):收集整个Java堆和方法区的垃圾收集。
下面逐个介绍下4种回收算法:
定义:标记清除算法顾名思义c;是指在虚拟机执行垃圾回收的过程中c;先采用标记算法确定可回收对象c;然后垃圾收集器根据标识c;清除相应的内容c;给堆内存腾出相应的空间。
缺点:(容易产生大量的内存碎片c;可能无法满足大对象的内存分配c;一旦导致无法分配对象c;那就会导致jvm启动gc)。
第一个是执行效率不稳定c;如果Java堆中包含大量对象c;而且其中大部分是需要被回收的c;这时必须进行大量标记和清除的动作c;导致标记和清除两个过
程的执行效率都随对象数量增长而降低;
第二个是内存空间的碎片化问题c;标记、清除之后会产生大量不连续的内存碎片c;空间碎片太多可能会导致当以后在程序运行过程中需要分配较大对象时无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。
标记-清除算法的执行过程(书中配图):
标记-整理:会将不被GC Root引用的对象回收c;清除其占用的内存空间。然后整理剩余的对象c;可以有效避免因内存碎片而导致的问题c;但是牵扯到对象的整理移动c;需要消耗一定的时间c;所以效率较低。
标记-整理算法的执行过程(书中配图):
复制算法:将内存分为等大小的两个区域c;FROM和TO(TO中为空)。先将被GC Root引用的对象从FROM放入TO中c;再回收不被GC Root引用的对象。然后交换FROM和TO。这样也可以避免内存碎片的问题c;但是会占用双倍的内存空间。
标记-复制算法的执行过程(书中配图):
把分代收集理论具体放到现在的商用Java虚拟机里c;设计者一般至少会把Java堆划分为新生代(Young Generation)和老年代(Old Generation)两个区域c;顾名思义c;在新生代中c;每次垃圾收集时都发现有大批对象死去c;而每次回收后存活的少量对象c;将会逐步晋升到老年代中存放。
长时间使用的对象放在老年代中(长时间回收一次c;回收花费时间久)c;用完即可丢弃的对象放在新生代中(频繁需要回收c;回收速度相对较快)c;如下图所示:
回收流程
新创建的对象都被放在了新生代的伊甸园中:
当伊甸园中的内存不足时c;就会进行一次垃圾回收c;这时的回收叫做 @H_236_7@minor GC:
@H_813_6@minor GC 会将伊甸园和幸存区FROM仍需要存活的对象先复制到 幸存区 TO中c; 并让其寿命加1c;再交换FROM和TO。伊甸园中不需要存活的对象清除:
交换FROM和TO:
同理c;继续向伊甸园新增对象c;如果满了c;则进行第二次Minor GC:
流程相同c;仍需要存活的对象寿命+1
:(下图中FROM中寿命为1的对象是新从伊甸园复制过来的c;而不是原来幸存区FROM中的寿命为1的对象c;这里只是静态图片不好展示c;只能用文字描述了)
再次创建对象c;若新生代的伊甸园又满了c;则会再次触发 Minor GC(会触发 stop the worldc; 暂停其他用户线程c;只让垃圾回收线程工作)c;这时不仅会回收伊甸园中的垃圾c;还会回收幸存区中的垃圾c;再将活跃对象复制到幸存区TO中。回收以后会交换两个幸存区c;并让幸存区中的对象寿命加1!
如果幸存区中的对象的寿命超过某个阈值(最大为15c;4bit)c;就会被放入老年代中:
如果新生代老年代中的内存都满了c;就会先触发Minor Gcc;再触发Full GCc;扫描新生代和老年代中所有不再使用的对象并回收:
分代回收小结:
+1
c;并且交换FROM和TO。15
时c;会晋升至老年代。后续会陆续更新c;这本书的笔记记的差不多了c;排版和格式需要花时间整理c;文章都会同步到公众号上c;也欢迎大家通过公众号加入我的交流qun互相讨论jvm这块的知识内容!
以上是大佬教程为你收集整理的深入理解JVM虚拟机读书笔记——垃圾回收算法全部内容,希望文章能够帮你解决深入理解JVM虚拟机读书笔记——垃圾回收算法所遇到的程序开发问题。
如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。