一、Java的回收策略是:
回收已经“死了”的对象即不可能再被任何途径使用的对象
二、如何判断对象已经死了:
我们现在常用的方法是引用计数算法和根搜索算法
①引用计数算法的原理和弊端 :
原理:我们给对象 添加一个引用计数器,每当一个地方引用这个对象的时候,我们的计数器,就加1,当引用失效的时候,我们的计数器的值就减1;任何时刻计数器为0的对象就是不可能再被使用的,我们就判定为该对象已“死”;优点,实现简单,判定效率高;
弊端:很难解决对象间相互循环引用的问题;例如:对象A和对象B都有test 字段,领A.test = B.test;B.test = A.test;除此之外这两个对象再无任何引用;实际上这两个对象已经是 应该回收的 对象,但是他们相互引用,导致他们的计数器都不为0,这种情况下,引用计数算法就无法很好的解决这类问题;
②可达性分析算法:算法的基本思路就是通过一系列 成为GCRoots的对象那个作为根节点,从这些节点开始向下搜索,搜索的路径成为引用链,当一个对象到GCR哦哦图书没有任何引用链的时候,则认为此对象是不可用的;
③即使可达性分析算法判定为不可达的对象,也不是非死不可的,这时候是一个缓刑阶段,至少要经历两次标记的过程,如果可达性分析判定其没有与GCRoots相连的引用链的时候,则进行第一次标记并进行筛选,筛选的条件是此对象有没有必要执行finalize()方法。当对象没有覆盖finalize()方法或者finalize()方法已经被虚拟机调用过,虚拟机将这两种情况都判定为“没有必要执行”;如果这个finalize()方法被判定为有必要执行 finalize()方法,那么这个对象将会被方最一个F-Queue的队列中,并在稍后由一个虚拟机自动创建的、低优先级的Finalizer县城去执行它;
④finalize()方法是对象逃脱死亡命运的最后一次机会,如果对象在finalize()方法中重新与引用链上的任何一个对象建立关联,则成功自救,否则这个对象基本上就真的被回收了;
finalize()的测试方法如下:
public class FinalizeEscapeGC { public static FinalizeEscapeGC SAVE_HOOK = null; public void isAlive(){ System.out.println("yes,i am alive"); } @Override protected void finalize() throws Throwable { super.finalize(); System.out.println("finalize method executed"); FinalizeEscapeGC.SAVE_HOOK = this; } public static void main(String[] args) throws InterruptedException { SAVE_HOOK = new FinalizeEscapeGC(); //对象的第一次自救 SAVE_HOOK = null; System.gc(); //因为finalize()的优先级很低 ,所以暂停1秒用来等待它 Thread.sleep(1000); if (SAVE_HOOK != null){ SAVE_HOOK.isAlive(); }else { System.out.println("no,i am dead"); } //与对象的第一次自救的代码完全相同,却失败了 SAVE_HOOK = null; System.gc(); Thread.sleep(1000); if (SAVE_HOOK != null){ SAVE_HOOK.isAlive(); }else { System.out.println("no,i am dead"); } } /** * 执行结果第一成功,第二次失败,因为任何一个对象的finalize()方法都只会被系统调用一次,如果对象面临下一次回收,他的finalize() * 方法不会再被执行,因此第二次失败 */ }
运行结果为:
finalize method executed yes,i am alive no,i am dead Process finished with exit code 0
虽然finalize()这个方法已经很悲情了,但是这个方法并不是一个好的方法,我们要尽量的避免使用它,因为他的运行代价高,不确定性大,无法保证各个对象的调用顺序,有些教材中描述他为“关闭外部资源”之类的工作,这完全是对这个方法的一种自我安慰,finalize()能做的所有工作,使用try-finally或者其他方式都可以做的更好更及时,所以我们可以忘了Java中拥有这样一个方法;
三、回收方法区
很多人认为方法区(或者Hotpot虚拟机中的永久代)是没有垃圾收集的,并且Java虚拟机规范中说过可以不要求虚拟机在方法区进行垃圾收集,并且在方法区中进行垃圾收集的性价比一般非常低;
永久代的垃圾收集主要回收两部分内容:废弃常量和无用的类
①回收废弃常量:
与回收Java堆中的对象十分类似;一常量池中字面量的回收为例,假如一个字符串“ABC”已经进入了常量池,但是当前系统中没有任何一个String对象是这个的,如果这时发生垃圾回收,而且必要的话,这个字符串常量会被系统清理出常量池,常量池中的其他类(接口)、方法、字段的符号引用也与此类似;
②回收无用类
:该类在所有的实例都已经被回收,也就是Java堆中不存在该类的任何实例
:加载该类的ClassLoader已经被回收
:该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射来访问该类的方法
满足以上三个条件后,并不是必然会被回收是否被回收还要被虚拟机通过一些参数进行控制
四、垃圾收集算法
1、标记-清除法
这是最基础的算法,分为标记和清除两个阶段:首先标记处所有需要回收的对象,在标记完成后同意清除这些被标记的对象;
这个方法主要有两个不足:一个是清除效率过于低下,这两个过程的效率都不高;另外一个就是空间问题了,标记清除过后会产生大量的内存碎片,肯能导致以后在程序运行过程中需要分配较大对象的时候,无法找到足够的连续内存而 不得不提前出发另一次垃圾收集动作;
2、复制算法
为了解决效率问题,它将可用内存按照容量划分为大小相等的两块,每次只使用其中的一块。当一块的内存用完了,就将还存活着的对象复制到另一块上面,然后再把已经使用过内存空间一次清理掉。这样使得每次都是对整个半区进行内存回收,内存分配的时候也就不用考虑内存碎片等复杂情况,只要移动堆顶指针,按顺序分配内存即可,实现简单,运行高效;
因为将内存缩小为了原来的一半,这样浪费太高了,因为新生代中的对象98%都是朝生夕死的,所以并不需要按照一比一的比例来华反内存空间,而是将内存分为一个Eden空间,两个Survivor空间,每次使用Eden空间和其中的一块Survivor空间。当回收时,将Eden和Survivor中还存活的对象一次性复制到另一块Survivor空间上,最后才清理掉Eden空间和刚才使用过的Survivor空间。HotSpot虚拟机默认的Eden和Survivor的比例是八比一,也就是每次只有10%的会被浪费,但是98%只是一般情况下,当特殊情况的时候,当Survivor空间不够用的时候,需要依赖其他没存(这里指老年代)进行分配担保;这些多出来的对象将直接通过分配担保机制进入老年代;
3、标记-整理算法
复制收集算法在对象存活率较高时就要进行较多的复制操作,效率将会变低。如果不想浪费50%的空间,就需要有额外的空间进行分配担保,以应对被使用过的内存中的对象都100%存活的极端情况,所以老年代中不能直接选用这种算法;
标记-整理算法也是分为两个阶段:第一阶段与标记-清除算法的第一阶段一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉边界以外的内存;
4、分代收集算法
目前的商业虚拟机的垃圾收集都采用“分代收集”算法,该算法就是根据对象存活周期不同将内存划分为几个块,一般把Java堆区分为新生代和老年代,然后根据每一代的特点采用最适当的收集算法;新生代采用复制算法,老年代因为存活率较高、没有额外空间对其进行担保,于是采用标记-整理或标记-清除算法
相关推荐
基于动态优先级的实时Java垃圾回收策略.pdf 本文提出了一种基于动态优先级的实时Java垃圾回收策略,旨在解决通用平台下Java虚拟机垃圾回收器(GC)的不定期启动问题。该策略将优先级分为两级:关键线程和非关键线程...
#### 四、垃圾回收策略 ##### 4.1 分代收集理论 Java的堆内存被划分为新生代和老年代。新生代中的对象生命周期通常较短,因此这里频繁发生垃圾回收;而老年代的对象生命周期较长,垃圾回收频率较低。分代收集理论...
- Java的垃圾回收机制通常采用分代收集策略,即根据对象的存活时间将其分为不同的代(如新生代、老年代),并对不同代的对象采取不同的回收策略。 - 常见的垃圾回收算法包括标记-清除算法、复制算法以及标记-整理...
本教程将涵盖Java的基础知识,特别是关于内存管理的重要概念——Java内存区域、Out of Memory (OOM)错误以及垃圾回收器和垃圾回收策略。 1. **Java入门**: Java的学习始于基础语法,包括变量、数据类型、运算符、...
Java垃圾回收器(Garbage Collector, GC)是Java编程语言中的一个重要特性,它负责自动管理内存,自动回收不再使用的对象,以防止内存泄漏。在Java中,程序员无需手动释放内存,这一过程由JVM(Java虚拟机)自动完成...
该算法的主要思想是,根据对象的生命周期将其分为不同的代(Generation),然后根据不同的代采用不同的垃圾回收策略。该算法的优点是可以有效地防止内存泄露和有效地使用空闲的内存。 Java中的垃圾回收机制主要使用...
### Java垃圾回收详解 #### 垃圾回收基础概念 在Java编程语言中,垃圾回收(Garbage Collection, GC)是一项自动化的内存管理...通过合理运用各种垃圾回收策略和引用类型,开发者可以构建更加高效、健壮的应用程序。
"深入了解java内存分配和回收策略" java内存分配是java技术体系中的一个核心问题,java技术体系中所提到的内存自动化管理归根结底就是内存的分配与回收两个问题。下面我们将深入了解java内存分配和回收策略。 一、...
Java垃圾回收机制是Java编程中一个非常重要的概念,尤其在面试和实际开发中常常被讨论。垃圾回收(Garbage Collection, GC)是Java虚拟机自动管理内存的一种方式,旨在自动识别并释放不再使用的对象,从而避免内存...
### Java垃圾回收及内存泄漏知识点详解 #### 一、Java内存管理 1. **运行时数据区**:Java虚拟机管理的内存主要分为以下几个部分: - **方法区(Method Area)**:存储类的信息(如类名、字段、方法等)、常量、...
Java垃圾回收机制是Java编程中至关重要的一部分,它自动管理内存,释放不再使用的对象,避免内存泄漏,并优化内存使用。Java虚拟机(JVM)的堆内存是存放对象的主要区域,当对象通过new等指令创建后,垃圾回收机制...
Java垃圾回收机制(GC)是Java编程语言的关键特性,它自动管理内存,释放不再使用的对象,以防止内存泄漏。GC的运作方式主要有两种策略:引用计数和对象引用遍历。 引用计数是一种简单但不完美的方法。每个对象都有...
Java和C#都采用了类似的分代垃圾回收策略,但具体实现细节有所不同。Java的垃圾回收更加细致地划分了内存区域,提供了更多的配置选项,以满足不同场景的需求;而C#则更注重于简化配置,通过默认设置来提供较好的性能...
JVM会根据程序运行的实际情况选择合适的垃圾回收策略。例如,“标记-清除”适合于对象生命周期较长的情况,而“复制-清除”则适用于对象生命周期较短,频繁创建和销毁的场景,因为它能提供更好的内存碎片管理。 总...
该包支持多种操作,包括但不限于加载和卸载类、执行Java方法以及管理Java安全策略等。其中,权限管理是一个重要的组成部分,它允许管理员授予或撤销Java应用程序对特定资源的访问权限。 #### 三、权限管理概述 在...
内存分配与回收策略, JVM 调优, 文件结构, 类加载机制, Java 程序 Java是一种面向对象的编程语言,由Sun Microsystems于1995年推出。它是一种跨平台的语言,意味着可以在不同的操作系统上运行。Java具有简单、...
### Java垃圾回收机制详解 #### 一、引言 在软件开发领域,特别是对于像Java这样的面向对象语言,内存管理一直是开发者关注的核心问题之一。Java的出现极大地简化了这一过程,其中最为突出的特点之一就是其内置的...