当前位置:首页 > 嵌入式培训 > 嵌入式学习 > 讲师博文 > 浅谈Java垃圾回收机制

浅谈Java垃圾回收机制 时间:2019-03-05      来源:华清远见

一、浅谈java中的垃圾回收机制

根据很多同学与朋友,日常工作与面试时经常会被问到JVM垃圾回收机制,接下来博主就给你捋一捋,疏理知识点,如有不同意见或建议,欢迎联系博主以便交流。

1.GC技术背景

浅谈JVM垃圾回收的前世今。说起垃圾回收机制(GC),大部分人都把这项技术当做Java语言的伴生产物。事实上,GC的历史比Java久远,早在1960年Lisp这门语言中就使用了内存动态分配和垃圾回收技术。

2.JVM内存回收

程序员们知道,jvm内存结构分为五大区域:程序计数器、虚拟机栈、本地方法栈、堆区、方法区。其中虚拟机栈、本地方法栈与程序计数器这3个区域随线程而生、随线程而灭,因此就不需要考虑过多内存垃圾回收问题,因为一个方法调用结束或者线程结束时,内存自然就跟随着回收了。

我们就把重点放在方法区与堆区,这部分内存的分配和回收是动态的,正是垃圾收集器所需关注的部分。

二、GC中的算法

1.直面问题,哪些可以回收,哪些暂时还不能回收?

垃圾收集器在对堆区和方法区进行回收工作前,首先肯定确定这些区域内对象哪些可以被回收,哪些暂时还不能回收,这时就要用到判断对象是否存活的算法!如何失去任何引用,垃圾收集器就把它收走。

(1)引用计数算法

早期策略。在这种算法中,堆中每个对象实例都有一个引用计数。当一个对象被创建时,就将该对象实例分配给一个变量,该变量计数设置为1。当任何其它变量被赋值为这个对象的引用时,计数加1(但当一个对象实例的某个引用超过了生命周期或者被设置为一个新值时,对象实例的引用计数器减1。任何引用计数器为0的对象实例可以被当作垃圾收集。当一个对象实例被垃圾收集时,它引用的任何对象实例的引用计数器减1。

缺点:循环引用时无效

如:如父对象有一个对子对象的引用,子对象反过来引用父对象。

(2)可达性分析算法

可达性分析算法是从离散数学中引入的,也是如今正在使用的策略,程序把所有的引用关系看作一张图(DOM图类似),从一个节点GC ROOT开始,寻找对应的引用节点,找到这个节点以后,继续寻找这个节点的引用节点,就如递归思想一般,遍历所有,当所有的引用节点寻找完毕之后,剩余的节点则被认为是没有被引用到的节点,即无用的节点,无用的节点将会被判定为是可回收的对象。

三、引用与回收问题

无论是通过引用计数算法判断对象的引用数量,还是通过可达性分析算法判断对象的引用链是否可达,判定对象是否存活都与“引用”有关。

当对象失去所有引用时,我们就可以说对象生命周期结束了,即为死亡,就该回收它啦。

四、常见的垃圾回收器

1.Serial收集器

新生代单线程收集器,标记和清理都是单线程,优点是简单高效。是client级别默认的GC方式。

2.Serial Old收集器

老年代单线程收集器,Serial收集器的老年代版本。

3.ParNew收集器

新生代收集器,可以认为是Serial收集器的多线程版本,在多核CPU环境下有着比Serial更好的表现。

4.Parallel Scavenge收集器

并行收集器,追求高吞吐量,高效利用CPU。吞吐量一般为99%, 吞吐量= 用户线程时间/(用户线程时间+GC线程时间)。适合后台应用等对交互相应要求不高的场景。是server级别默认采用的GC方式。

5.Parallel Old收集器

Parallel Scavenge收集器的老年代版本,并行收集器,吞吐量优先。

6.CMS(Concurrent Mark Sweep)收集器

高并发、低停顿,追求最短GC回收停顿时间,cpu占用比较高,响应时间快,停顿时间短,多核cpu 追求高响应时间的选择。

五、垃圾回收机制什么时候触发?

由于会遇到的问题不一样,因此垃圾回收区域、时间、方法也不一样。GC有两种类型:Full GC  &  Scavenge GC 。

1.FULL GC  

GC在优先级最低的线程中运行,一般在应用程序空闲即没有应用线程在运行时被调用。

对整个堆进行整理,包括Young(新生区)、Tenured(老年区)和Perm(静态区)。Full GC因为需要对整个堆进行回收,所以比Scavenge GC要慢,因此应该尽可能减少Full GC的次数。在对JVM调优的过程中,很大一部分工作就是对于Full GC的调节。

但下面的条件例外。

2.Scavenge GC

(1)Java堆内存不足时,Scavenge GC会被调用。

(2)当应用线程在运行,并在运行过程中创建新对象,若这时内存空间不足,JVM就会强制调用Scavenge GC线程。若GC一次之后仍不能满足内存分配,JVM会再进行两次GC,若仍无法满足要求,则JVM将报“out of memory”的错误,java虚拟机将停止运行。

六、GC的两个重要方法

(1)System.gc()方法

  使用System.gc()可以不管JVM使用的是哪一种垃圾回收的算法,都可以请求Java的垃圾回收。

在命令行中有一个参数-verbosegc可以查看Java使用的堆内存的情况,由于这种方法会影响系统性能,不推荐使用。

(2)finalize()方法

  在JVM垃圾回收器收集一个对象之前,一般要求程序调用适当的方法释放资源,但在没有明确释放资源的情况下,Java提供了缺省机制来终止该对象心释放资源,这个方法就是finalize()。

它的原型为:protected void finalize() throws Throwable   

在finalize()方法返回之后,对象消失,垃圾收集开始执行。之所以要使用finalize(),是存在着垃圾回收器不能处理的特殊情况。

七、GC使用与性能优化管理

(1)不要显式调用System.gc()。此函数建议JVM进行主GC,虽然只是建议而非一定,但很多情况下它会触发主GC,从而增加主GC的频率,也即增加了间歇性停顿的次数。大大的影响系统性能。

(2)尽量减少临时对象的使用。临时对象在跳出函数调用后,会成为垃圾,少用临时变量就相当于减少了垃圾的产生,从而延长了出现上述第二个触发条件出现的时间,减少了主GC的机会。

(3)对象不用时最好显式置为Null。一般而言,为Null的对象都会被作为垃圾处理,所以将不用的对象显式地设为Null,有利于GC收集器判定垃圾,从而提高了GC的效率。

(4)尽量使用StringBuffer,而不用String来累加字符串。由于String是固定长的字符串对象,累加String对象时,并非在一个String对象中扩增,而是重新创建新的String对象,如Str5=Str1+Str2+Str3+Str4,这条语句执行过程中会产生多个垃圾对象,因为对次作“+”操作时都必须创建新的String对象,但这些过渡对象对系统来说是没有实际意义的,只会增加更多的垃圾。避免这种情况可以改用StringBuffer来累加字符串,因StringBuffer是可变长的,它在原有基础上进行扩增,不会产生中间对象。

(5)能用基本类型如Int,Long,就不用Integer,Long对象。基本类型变量占用的内存资源比相应对象占用的少得多,如果没有必要,最好使用基本变量。

(6)尽量少用静态对象变量。静态变量属于全局变量,不会被GC回收,它们会一直占用内存。

(7)注意分散对象创建或删除的时间。集中在短时间内大量创建新对象,特别是大对象,会导致突然需要大量内存,JVM在面临这种情况时,只能进行主GC,以回收内存或整合内存碎片,从而增加主GC的频率。集中删除对象,道理也是一样的。

八、结束语

Java垃圾回收机制靠一篇博文很难讲解完,虽然本人做了很大努力,但是还得投降,对于各个垃圾收集器的区别、运行过程中各内存区域参数的设置、GC日志的查看等内容,希望继续关注博客,联系博主,我会不断的更新的!!

上一篇:C语言中的逻辑值

下一篇:C语言:chmod命令

热点文章推荐
华清学员就业榜单
高薪学员经验分享
热点新闻推荐
前台专线:010-82525158 企业培训洽谈专线:010-82525379 院校合作洽谈专线:010-82525379 Copyright © 2004-2018 北京华清远见科技发展有限公司 版权所有 ,京ICP备16055225号,京公海网安备11010802025203号