怎么判定对象是否为垃圾对象
1.引用计数法
在每个对象中添加一个引用计数器,当有地方引用这个对象的时候,引用计数器就+1,当引用失效的时候,计数器的值就-1。任何时刻计数器为0的对象就是不可能再被使用的。优点:容易实现
缺点:很难解决对象之间相互循环引用的问题
2.可达性分析
目前的JAVA虚拟机是使用可达性分析算法来判定对象是否存活的,这个算法的基本思路就是通过一系列被称为(GCRoot)的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径成为引用链,当一个对象没有任何引用链相连,则证明此对象是不可用的,回收。
那些对象可以作为GCRoot?
(1)虚拟机栈中的引用对象(我们都知道,java虚拟机在调用方法的时候都会创建一个相应的栈帧,栈帧中包含了这个方法内部使用的所有对象的引用,一旦方法执行结束之后,这些临时对象引用也就不复存在了,这些对象就会被垃圾收集器给回收。)
(2)方法区中的类静态属性引用的对象。 (一般指被static修饰的对象,加载类的时候就加载到内存中。)
(3)方法区中的常量引用的对象。(final修饰的对象)
(4)本地方法栈中的JNI(native方法)引用的对象
3.对象死亡需要经过两个过程
(1)可达性分析后没有发现引用链
(2)查看对象是否有finalize方法,如果有重写且在方法内完成自救(如再建立引用),还是可以抢救一下。
垃圾清除算法
1.标记清除算法(Mark-Sweep)
实现很简单,第一步对需要回收的对象进行标记,第二步直接清除标记的对象,问题也很明显,会产生内存碎片
![](/i/l/?n=22&i=blog/2358073/202208/2358073-20220825211942557-227752966.png)
2.复制算法(copying)
将内存分为两块区域(运行区域和保留区域),每次只是使用运行区域,垃圾回收时标记所有存活的对象,之后复制到保留区域,然后清空运行区域,最后交换两块空间的角色。
效率高,不会有内存碎片,但是内存利用率低。
3.标记整理算法(Mark-Compact)
首先对需要回收的对象进行标记,把所有存活的对象压缩到内存的另一端,之后清除边界之外所有的对象因为做了一次整理操作,所以垃圾对象清除后,解决内存碎片的问题,同时也不存在空间浪费的问题。但是整理这步操作,本身会消耗资源,甚至如果内存中存活的对象很多,并且都是一些占内存空间较小的对象,要回收的垃圾对象也很少时,垃圾收集器要移动大量的存活对象才能换取少量的内存空间,实在是不划算。
![](/i/l/?n=22&i=blog/2358073/202208/2358073-20220825213057686-1698277330.png)
分代回收机制
当前商业虚拟机的垃圾回收器,大多遵循“分代收集”的理论来进行设计,这个理论大体上是这么描述的:a. 绝大部分的对象都是朝生夕死。(新生代)
b. 熬过多次垃圾回收的对象就越难回收。(老年代)
根据以上两个理论,朝生夕死的对象放一个区域,难回收的对象放另外一个区域,这个就构成了新生代和老年代。
1.新创建的对象尝试放到eden区,如果该对象比eden区总量都大,那么直接放到老年代。(原因有两点,第一点是大对象需要连续的内存空间,而新生代为了安放大对象可能需要多次进行GC,增加开销;第二点是新生代种伊甸园区和幸存者区常采用复制算法,需要经常复制对象到不同的区域,而大对象在复制时开销较大。)
2.当eden区没有足够的空间时,触发一次minorGC,将eden区和from区存活的对象移动到to区,对象年龄+1(对象头中),然后将eden区和from区进行回收,最后from区和to区互换。
3.如果to区没有足够的空间时,那么将满足条件的对象移入老年代(对象的年龄达到了一定数值 CMS默认6,其他垃圾回收器默认15,这个可以通过-XX:MaxTenuringThreshold=10设置,但是最大值为15)
4.移动过程中老年代空间也不足了,需要回收老年代(majorGC),往往回收老年代时会降整个堆一并回收(fullGC)。
垃圾回收器
1.
标签:对象,引用,回收,算法,内存,机制,垃圾 From: https://www.cnblogs.com/xinay/p/16625707.html