Java语言中 的垃圾收集器相对于以前的其他语言优势是什么?
过去的语言需要程序员显示的进行分配内存、释放内存。这种做法可能会引起“内存泄漏”,即由于某种原因是分配给程序的内存无法释放,如果该任务不断的重复进行,程序的内存将会耗尽而导致程序异常终止,甚至无法继续工作,相比之下:Java语言不要求程序员显示的调用内存分配和释放内存,避免了很多潜在的问题。Java在创建对象的时候会自动的分配内存,并当对象的引用不存在的时候释放这块内存。
Java中使用被称为垃圾收集器的技术来监视Java程序的运行,当对象不再使用的时候就自动释放对象所使用的内存,一般情况下无需显示的请求垃圾收集器,程序运行时垃圾收集器会不定时的检查对象的各个引用。
注意:GC即是垃圾收集机制,是指JVM用于释放那些不再使用的对象的内存,Java语言并不要求JVM有GC,也没有规定GC是如何工作的,但是我们通常使用的JVM都是有GC的;Java的垃圾收集机制是为所有的Java应用进程服务的,并不单单是针对某一个特定的服务。
垃圾回收并不是一个独立的平台,它具有平台依赖性。
在Java语言中,判断一块内存空间时候符合垃圾收集器收集标准的标准只有一下两个:
- 给对象赋予了空值null,以后在没有调用过。
- 给对象赋予了新的值,即重新分配了内存空间。
注意:一块内存空间符合垃圾收集器 的收集标准,并不意味着这块内存空间就一定会被垃圾收集器收集。
我们在使用垃圾收集器时需要注意以下几点:
- 不要试图去假定垃圾收集器发生的时间。这一切都是未知的
- Java中提供了一些和垃圾收集机制打交道的类,而且提供了一种强制执行垃圾收集的方法—调用System.gc(),但这同样是一个不确定的方法,请求是一回事,能不能得到真正的执行垃圾收集还是一个未知数
- 可以自己挑选合适自己的垃圾收集器
- 对于频繁申请内存和释放内存的操作,还是要自己控制,最好使用finalize()强制执行比较好。
什么是Java中的内存泄露?
对于Java程序员来说,GC是透明的,不可见的。虽然有几个函数能够访问GC 根据Java语言规范定义,该函数不保证虚拟机的垃圾收集器一定会执行,因为不同的虚拟机实现者可能使用不同的算法进行管理,GC现成的优先级别比较低,虚拟机调用GC的策略有很多 ,但是执行垃圾回收会影响系统的性能。
垃圾收集器和内存分配策略:
一、 那些内存需要回收?
程序计数器、虚拟机栈和本地方法栈3个区域随线程而生,随线程而灭;栈中的栈帧随着方法的进入和退出有条不紊的执行着出栈和进栈的操作。每个栈帧分配多少内存基本上是在类结构确定下来时就已经确定了。因此这几个区域的内存分配和回收都具备确定性,在这几个区域就不需要过多的考虑回收的问题,因为方法或者线程 的结束内存自然就会被回收回去。
但是堆区域和方法区则不一样,这部分内存分配和回收都是动态的,垃圾收集器所关注的也主要是这部分区域的垃圾回收过程。
二、什么时候进行回收?
- 如何判断对象已死?
- 引用计数算法:给对象添加一个引用计数器,每当有一个地方应用该对象的时候计数器的值就加1,当引用失效的时候计数器的值就减1,任何时候计数器的值为0的对象就是不能在被使用的对象。这种方法的实现比较简单判断的效率也很高,但是主流的虚拟机都不选择这种方法的主要原因就是它很难解决对象之间相互循环引用的问题。
- 可达性分析算法:主流的商用语言中使用的一般是可达性分析算法来判断对象是否存活。这个算法的基本思路是通过一系列的被称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径成为引用链,当一个对象到GC Roots没有任何引用链相连的时候证明这个对象是不可用的,能被判定为可回收的对象。
在Java语言中,能作为GC Roots的对象包括以下几种:
- 虚拟机栈中引用的对象;
- 方法区中类静态属性应用的对象;
- 方法区中常量引用的对象;
- 本地方法栈中JNI(Native方法)引用的对象
Java对引用概念的划分:强引用、软引用、弱引用、虚引用。这四种引用强度一次逐渐减弱。
- 强引用的对象永远不会被垃圾收集器回收掉。
- 软引用是用来描述一些有用但并非必需的对象,对于软引用连接的对象在系统将会发生溢出之前会把这些对象列为可回收对象范围之内,如果这次回收还是没有足够的内存,才会发生溢出。
- 弱引用也是用来描述非必需对象的,但是他的强度比软引用强度还要弱一些,被弱引用关联的对象只能生存到下一次垃圾收集之前。
- 虚引用是最弱的一种引用关系。一个对象是否有虚引用的存在完全不会影响其生存时间,设置虚引用的唯一目的就是在这个对象被垃圾收集器回收的时候能得到一个系统通知。
对方法区的回收:Java虚拟机规范中确实不要求虚拟机方法在方法区实现垃圾收集。而且在方法区的垃圾收集效率是比较低的,方法区(HotSpot的永久代)的垃圾收集主要回收两部分的内容:废弃常量和无用的类,通常判定一个常量是废弃的常量比较简单,但是要判定一个类是无用的类需要三个条件:该类所有的实例都已经被回收、加载该类的ClassLoader已经被回收、该类所对应的Class类没有在任何地方被引用。频繁自定义ClassLoader 的场景都需要虚拟机具备类卸载的功能。
三、 如何对这些需要回收的内存进行回收?
可达性分析算法中不可达的对象也不一定会被回收,真正被回收需要经过两次标记过程。如果对象再经过可达性分析算法之后没有与GC Roots相连接的引用链,它会被第一次标记并进行一次筛选,筛选的条件就是此对象时候有必要执行finalize()方法。如果对象在该方法中重新与引用链建立了连接那就会被移除即将回收的集合,反之就会被真的回收。
四、垃圾收集算法:
1、标记-清除算法(mark-sweep):分为标记和清除两个阶段,首先标记出所有需要回收的对象,在标记完成之后统一回收所有被标记的对象。这是一种最基础的垃圾收集算法。这个算法的两个不足之处是:一个是效率问题,标记和清除的效率都不高、另一个是空间问题,标记清楚之后会产生大量的不连续的内存碎片,空间碎片过多可能会导致后边程序如果需要分配大的空间的时候无法找到满足条件的连续内存而不得不提前触发另一次的垃圾收集动作。
2、复制算法:为了解决效率问题,