众所周知,Java有自己的垃圾回收机制,它可以有效的释放系统资源,提高系统的运行效率。那么它是怎么运行的呢,这次就来详细解析下Java的垃圾回收

1.什么是垃圾?

垃圾回收回收的自然是垃圾,那么java中是垃圾指的是什么呢?java中的垃圾指的是内存中不再使用的对象,这些对象不再会被使用,但是依然存在于内存中,如果不及时清理,久而久之会使得内存中存在大量的无用对象,显然会影响系统的正常运行

2.垃圾是如何判定的?

既然知道了内存中不再使用的对象就是垃圾,那么JVM是如何从堆内存中找到这些垃圾的呢?主要有两种方法:

2.1:引用计数算法

引用计数算法是垃圾收集器比较早的方法,堆内存中的所有对象实例都有一个引用计数,当任何其他变量被赋值为这个对象的引用时,这个对象的计数就+1,当一个对象实例
的某个引用超过了生命周期或被设置为其他新值时,对象的实例引用计数-1,当对象实例的引用计数为0时,就会被当做垃圾回收,如下示例
public static void gcTest1(){
User user = new User();//创建一个User对象实例,并新建User变量引用这个对象,此时引用计数+1之后=1
user = new User();//user变量执行了一个新的User对象实例,上一行中创建的User对象实例引用计数-1=0,会被垃圾回收
}

但是引用计数算法会有循环引用的问题,如
User user1 = new User();
User user2 = new User();
user1.user = user2;
user2.user = user1;

由于user1和user2互相引用对方,导致他们的引用计数器始终不会为0,这样垃圾收集器就永远无法回收它们。

2.2可达性分析算法

可达性分析算法是将所有的引用关系看做一张图,从GC Root节点开始寻找对应的引用节点,找到节点之后继续寻找这个节点的引用节点,当所有的引用节点寻找完后,剩余的没有被寻找到的节点就是不可达节点,不可达的节点就会被判定为可以回收的对象,可以作为GC ROOT节点的对象由四种分别如下:
a.虚拟机栈中引用的对象(栈帧中的本地变量表)
b.方法区中类静态属性引用的对象
c.方法区中常量引用的对象
d.本地方法栈中的JNI(Native方法)引用的对象

而即使可达性分析算法中剩下的不可达对象也并非直接被垃圾回收,而只是暂时处于“缓刑”阶段,真正被垃圾回收至少还需要经历两次标记的过程
第一次标记:当对象经过可达性分析被判定位不可达对象之后,会被第一次标记
第二次标记:第一次标记之后,会继续判断该对象是否有必要执行finalize()方法,在finalize()方法中没有重新与引用链建立关联关系的会被第二次标记

被第二次标记之后的对象就会被垃圾收集器进行垃圾回收处理

3.垃圾怎么回收

上一段是关于怎么寻找垃圾的,那么找到垃圾之后需要怎么进行回收呢,主要分为四种收集算法

3.1、标记-清除算法(Mark-Sweep)

标记-清除算法是从所有的GC-ROOT集合进行扫描,对可达的对象进行标记表示是存活的,标记完后再扫描整个内存中未被标记对象进行回收。标记-清除算法只对不存活的对象进行处理,而对于存活的对象不做任何处理,在存活对象较多的情况下效率较高,因为只需要操作少量的不存活的对象即可,但是同时也会造成内存碎片,所谓内存碎片是指内存中连续空间比较小的内存,如下图示:(绿色区域为存活对象,红色区域为垃圾对象)

通过标记-清除算法进行垃圾回收之后,将垃圾对象进行清除,此时内存地址为3和内存地址为7的位置是空闲的,如果此时新建一个对象需要占据内存为2,虽然位置3和位置7位置是空闲的,且加起来空闲内存也为2,但是由于不是连续的显然无法满足新对象的需求,所以新对象需要在地址12及之后来给它分配内存,而如果之后都不会再有内存为1的对象创建的话,那么内存地址3和7的位置就会一直无法得到使用,这两个位置就会被称作为内存碎片

所以这种算法的优点是操作简便,适用于存活对象较多,垃圾对象较少的情况,但是缺点是内存碎片化严重,

3.2、复制算法(Coping)

复制算法是将内存划分为两块大小相等的内存空间,每次只使用一块,当被使用的这块内存满了之后将这块内存上存活的对象复制到另一块内存上,然后然后再将这块内存清除掉,如下图

由于是将所要存活的对象依次复制到另一块内存,所以不会再有内存碎片问题,可以很有效的使用内存,但是这种算法需要将内存一分为二,一半可用而另一半必须是空闲状态。

而且如果内存中的存活的对象很多的话,需要每个存活对象都进行复制一次,效率会比较低,比较适用于存活对象较少而垃圾对象较多的场景

3.3、标记-整理算法(Mark-compact)

标记-整理算法是结合了以上两种算法优点而设计出来的,开始和标记-清除算法一样,先将所有需要回收的垃圾对象进行标记,然后不是进行清除,而是将存活对象移动到内存的一端,然后清除

端边界外的对象,如下图示:

很显然这种算法既不会出现内存碎片,同时又不需要将内存一分为二,同时满足了标记-清除算法和复制算法的优点。唯一的缺点就是需要将所要对象的位置进行移动

3.4、分代收集算法(Generational Collection)

分代收集算法是目前大部分JVM所采用的垃圾收集方法,它的核心思想是根据对象存活的生命周期将内存划分为若干个不同的区域。一般区域分为老生代(Old generation)和新生代(Young generation)

老生代的特点是每次垃圾回收时只要少量的对象需要被回收

新生代的特点是每次垃圾回收时都有大量的对象需要被回收

而针对不同区域再选择不同的收集算法进行垃圾收集。

新生代区域的内存划分是将内存按8:1:1的比例将内存划分为一个较大Eden区域和两个较小的Survivor区域,而两个Survivor区域分别叫From Space和To Space,如下图

对象的内存使用只会用到Eden区和其中的一个Survivor区,当进行垃圾回收时,会将Eden区和使用的Survivor区(From Space)中的所有存活的对象复制到另一块Survivor区(To Space)中去,此时Eden区和Survivor(From Space)的内存会全部清空,而只有另一块Survivor(To Space)会被使用,然后系统在运行一段时间之后,Eden区又会有很多对象,再进行垃圾回收的时候,Eden区会和之前保存数据的Survivor区(To Space)再将所有存活的对象复制到之前清空的Survivor区(From Space),而此时Eden和Survivor(To Space)又会被清空,反复这样操作,直到其中一块Survivor满了无法再存放对象的时候,就会直接把存活对象存放到老年代去,同时在新生代经过了15次垃圾回收后还依然存活的对象,也会被放到老年代。而老年代如果内存已经满了,则再触发一次垃圾回收(Full GC),Full GC的频率比较低,因为老年代中存活的对象一般生命周期都比较长。而在新生代触发的垃圾回收叫Minor GC,Minor GC触发的频率较高,因为新生代存放的大多数生命周期较短的对象。

针对新生代和老年代的特点,垃圾回收的算法也不一样,新生代存活对象较少,采用的是复制算法,老年代存活对象较多,采用的是标记-整理算法。分代收集算法如下图示:

当老年代区域满了的时候就会触发一次Full GC,不过由于老年代中存储的对象生命周期一般都比较长,所以Full GC的频率会比较低,回收的对象也比较少

现在知道了Java中的垃圾是什么,垃圾如何寻找以及使用何种算法及方式进行垃圾回收,那么垃圾回收的工具是什么呢?Java虚拟机靠的是垃圾收集器来进行垃圾回收,详情参看下一篇

tips:以上分析的都是针对堆内存的垃圾回收,实际上方法区也是会有垃圾回收的出现,比如废弃的常量以及没有被使用的类信息。

废弃的常量被垃圾回收的条件就是没有被引用;

类信息被垃圾回收的前提是:1.该类的所有实例已经被回收 2.加载该类的ClassLoader已经被回收 3.该类对应的Class对象在任何地方没有被引用,确保无法通过反射来访问该类的方法。

JVM探秘3---垃圾回收机制详解的更多相关文章

  1. JVM的垃圾回收机制详解和调优

    JVM的垃圾回收机制详解和调优 gc即垃圾收集机制是指jvm用于释放那些不再使用的对象所占用的内存.java语言并不要求jvm有gc,也没有规定gc如何工作.不过常用的jvm都有gc,而且大多数gc都 ...

  2. java面试题之----JVM架构和GC垃圾回收机制详解

    JVM架构和GC垃圾回收机制详解 jvm,jre,jdk三者之间的关系 JRE (Java Run Environment):JRE包含了java底层的类库,该类库是由c/c++编写实现的 JDK ( ...

  3. GC垃圾回收机制详解

    JVM堆相关知识    为什么先说JVM堆?  JVM的堆是Java对象的活动空间,程序中的类的对象从中分配空间,其存储着正在运行着的应用程序用到的所有对象.这些对象的建立方式就是那些new一类的操作 ...

  4. PHP的垃圾回收机制详解

    原文:PHP的垃圾回收机制详解 最近由于使用php编写了一个脚本,模拟实现了一个守护进程,因此需要深入理解php中的垃圾回收机制.本文参考了PHP手册. 在理解PHP垃圾回收机制(GC)之前,先了解一 ...

  5. JVM的内存区域划分以及垃圾回收机制详解

    在我们写Java代码时,大部分情况下是不用关心你New的对象是否被释放掉,或者什么时候被释放掉.因为JVM中有垃圾自动回收机制.在之前的博客中我们聊过Objective-C中的MRC(手动引用计数)以 ...

  6. 初识JVM:(二)Java的垃圾回收机制详解

    声明:本文主要参考https://www.cnblogs.com/codeobj/p/12021041.html 仅供个人学习.研究之用,请勿用于商业用途,如涉及侵权,请及时反馈,立刻删除. 一.Ja ...

  7. Java垃圾回收机制详解和调优

    gc即垃圾收集机制是指jvm用于释放那些不再使用的对象所占用的内存.java语言并不要求jvm有gc,也没有规定gc如何工作.不过常用的jvm都有gc,而且大多数gc都使用类似的算法管理内存和执行收集 ...

  8. Python垃圾回收机制详解

    一.垃圾回收机制 Python中的垃圾回收是以引用计数为主,分代收集为辅.引用计数的缺陷是循环引用的问题. 在Python中,如果一个对象的引用数为0,Python虚拟机就会回收这个对象的内存. #e ...

  9. php5.3新垃圾回收机制详解

    php的垃圾回收机制主要参考了http://blog.csdn.net/phpkernel/article/details/5734743 这文章. 变量对应的值,比如 $a="abc&qu ...

随机推荐

  1. RN Animated透明度动画

    主要代码解析: 如果我们希望吧Animated.Value从0变化到1,把组件位置从60px移动到0px,把不透明度从0编导1,就可以使用style的属性来实现 <Animated.Text s ...

  2. OC常用控件封装

    #import <Foundation/Foundation.h> #import <UIKit/UIKit.h> @interface CreateUI : NSObject ...

  3. 照葫芦画瓢之爬虫豆瓣top100

    import requestsimport reimport jsonfrom requests.exceptions import RequestExceptiondef get(url):    ...

  4. ABPIAbpSession

    AbpSession定义了几个关键属性: UserId:当前用户的Id或空(如果没有当前用户),如果调用需要授权的代码,它就不能为空. TenantId:当前租户的Id或空(如果没有当前租户:尚未登录 ...

  5. [django]http请求

    请求参数 http基本认证 https://zxc0328.github.io/2015/11/04/http-basic-auth/ 我们看到在http请求的header里有一个Authorizat ...

  6. centos 打包报错Can't connect to X11 window server using ':0.0' as the value of the DISPLAY variable.

    参考: https://blog.csdn.net/salonzhou/article/details/8990237https://stackoverflow.com/questions/23358 ...

  7. (转载)常用正则表达式大全!(例如:匹配中文、匹配html)

    正则匹配java注意点: 如果加 ^[\n]* 表示替换遇到 \n 的前后内容,如果加[\n]表示替换\n本处内容 原文地址:http://blog.csdn.net/dl020840504/arti ...

  8. 修改easydialog标题

    使用easyui作为前台框架极大的节省了项目资源,easyui官网文档中基本上囊括了所有的方法,但一些灵活性的方法文档中是找不到的,比如说动态替换窗口的属性,下边简单介绍些如何快速替换窗体的title ...

  9. ROSETTA使用技巧随笔--PyMOL实时观测ROSETTA模拟过程中的结构变化

    没有梦想的人,就是一只咸鱼,像我,就有一个梦想,就是让蛋白模拟过程变成动画,动起来! 虽然MD中有很多方法可以方模拟过程像动画一样播放出来,但是我一直想在ROSETTA中也找一个这样的功能,这不,我发 ...

  10. 关于 CGI,Fastcgi和php-fpm 理解

    首先,CGI是干嘛的?CGI是为了保证web server传递过来的数据是标准格式的,方便CGI程序的编写者. web server(比如说nginx)只是内容的分发者.比如,如果请求/index.h ...