电脑故障

位置:IT落伍者 >> 电脑故障 >> 浏览文章

有关于JVM的垃圾收集


发布日期:2023/5/21
 

Java中使用newnewarrayanewarray 和 multianewarray 指令来创建的对象当这些对象不再使用时由垃圾收集来释放那么 反序列化等都是间接使用了前面的某个指令 clone() 是个本地方法?

JVM规范不需要任何特定的垃圾收集技术甚至也没要求有垃圾收集机制大概只是说不需要手工释放内存具体怎么实现各 JVM 自行决定

GC 除了释放不再被引用的对象还要处理堆碎片整理出连续的空闲空间才能放得下新的对象不至于出现总的空闲空间足够但碎片太多而报出 Out of Memory 的异常

GC有两个好处一个是提高的生产率不用埋头于 Memory Link 的有时甚至是逐行的检查GC 也是 Java 安全策略的一部分有了它不至于因错误的释放内存而导至 JVM 崩溃但是 GC 的一个潜在缺陷影响了程序的性能它需要一直在后台不时的做些事情而且实时性也有所欠缺

垃圾收集算法

GC算法要做两件基本的事情 检测出垃圾对象 回收垃圾对象释放相应堆空间垃圾检测一般是先建立一个根对象集合其他对象要是从根对象起可触及就是活的无法到达的就是垃圾这里的根对象的认定就有些讲究的不同的 JVM 的看法不完全一致但总是会包含局部变量中的对象引用和栈帧的操作数栈(以及类变量中的引用)

另一个根对象的来源是被加载的类的常量池中的对象引用类的常量池中的字符串包括有类名超类名超接口名字段名字段特征签名方法名方法特征签名

还有一个来源是传递到方法中的没有被本地方法释放的对象引用(根据本地方法接口本地方法可以通过简单的返回来释放引用或者显式的调用一个回调函数来释放传递来的引用或是这两者的结合)

再一个潜在的根对象来源是JVM 运行时数据区中从垃圾收集器的堆中分配的部分某些实现中方法区中的类数据本身可能被存放在使用垃圾收集器的堆中以便使用和释放对象同样的垃圾收集算法来检测和卸载不再被引用的类

在某些 JVM 实现中像基本类型如一个 int 如果被解释为一个本地指针那它就是指向堆中的一个对象但是保守的垃圾收集器对这种基本类型引用的堆中的对象不处理

区别活动对象和垃圾的两个基本方法是引用计数和跟蹤

引用计数收集器

引用计数是垃圾收集的早期策略这种方法时堆中的每个对象都有一个引用计数当对象创建并赋给一个变量时引用计数为 每次赋给别的变量时引用计数加 当对象的引用超过了生存期或指向到了新值(如果引用置为 null)对象的引用计减 这样对象的引用计数为 时就是垃圾可清除的引用计数对多个对象的循环引用无能为力其实这一存对象都是死的但引用计数都不为 还有引用数的增减带来额外开销基于这些缺陷这种技术现在已经不为人所接受了

跟蹤收集器

跟蹤收集成追蹤从根节点开始的对象引用图基本的追蹤算法叫作标记并清除也就是垃圾收集的两个阶段标记阶段垃圾收集器遍历引用数标记每一个遇到的对象清除阶段未被标记的对象被释放可能在对象本身设置标记要么就是用一个独立的位图来设置标记

压缩收集器

垃圾收集同时要应对碎片整理的任务标记和清除通常使用两种策略来消除堆碎片压缩和拷贝这两种方法都是快速移动对象来减小碎片

压缩收集我想应该是在标记清除之后来做的?压缩收集器把活动对象越过空闲区滑到堆的一堆留下另一端的大的连续空闲块被移动的对象的引用也被更新指向新的位置更新被移动对象的引用有时通过一个间接对象引用层来实现的对象的引用不实际指向堆中对象而是指向一个对象句柄表(由它完成对象引用到堆中对象的实际位置的映射)对象被移动了只需要更新对象句柄表的句柄值这样程序中的对象引用不变这种方法简化了消除堆碎片的工作但是每次对象访问都要查一下映射表带来了性能上的损失

拷贝收集器

拷贝收集器把所有的活动对象移动到一个新的区域这种方法在追蹤对象过程中随着发现而被拷贝不再有标记和清除的区分一般的拷贝收集算法称为停止并拷贝在这个方案中堆被分为两个区域任何时候只使用其中一个区域对象在某一个区域中分配直到这个区域被耗尽时程序执行停止遍历这个区域活动对象移到另一个区域完成后程序恢复执行对象在新的区域分配原来的区域剩下垃圾全清除直到新的区域耗尽时程序停止活动对象又往回移循环工作这种方法的代价就是堆内存只能使用到一半

下面是停止和拷贝算法的垃圾收集过程中以时间为线索的 个快照

看过这个图应该不用多加解释反正就是堆分成上下两个区域哪部分满了活动对象往另一部分跑被移出的区域就成空的了来来回回新对象总是在正用的那部份分配想想你在运行 Java 程序的时候应该有过突然被中止不动的时候可能就是 GC 在活动了

分代收集器

简单的停止拷贝收集器的缺点是每次收集时所有的活动对象都要移动来移动去对于短生命的对象还好说经常可以就地解决掉可是对于长生命周期的对象就纯粹是个体力劳动了把它挪来挪去除消耗大量的时间没有产生任何效益分代收集能直接让长生命周期的对象长时间的呆在一个地方按兵不动GC 的精力可以更多的花在收集短命对象上

这种方法里堆被分成两个或更多的子堆每一个堆为一对象服务最年幼的那一代进行最频繁的垃圾收集因为多数对象是短命的只有很小部分的年幼对象可以在经历第一次收集后还存活如果一个最年幼的对象经历了好几次垃圾收集后仍是活着的那这个对象就成为寿命更高的一代它被转移到另外一个子堆中去年龄更高一代的收集没有年轻一代来得频繁每当对象在所属的年龄代中变得成熟(多次垃圾收集后仍幸存)之后就可以转移到更高年龄的一代中去

分代收集除了可应用于拷贝算法也可以应用于标记清除算法不管在哪种情况下把堆按照对象年龄分组可以提高最基本的垃圾收集的性能

上一篇:JTree树控件

下一篇:浅谈异常与恋爱