GC可谓是java相较于C++语言,最大的不同点之一。

1.GC回收什么?

上一篇讲了内存的分布。

其中程序计数器栈,虚拟机栈,本地方法栈 3个区域随着线程而生,随着线程而死。这些栈的内存,可以理解为在编译期已经确定。

方法结束,或者线程结束时,内存就自然被回收了。

一个interface的多个实现类,需要的内存可能不一样,一个方法的多个分支需要的内存也不一样,我们只有在程序运行的时候,才知道会创建那些对象,需要多少内存。

这部分分配和回收都是动态的,GC所关注的就是这部分内存。

2.回收的标准:

java堆里面几乎存放着所有的对象实例。对象实例如果已经不再被使用,那这段内存就应该被回收,这个时候就是GC上场的时候。

虚拟机栈,程序计数器,本地方法栈,这些都会随着线程的消亡而消亡。所以这些不需要考虑内存的回收。u

GC的回收特指Java堆 & 方法区的内存。

2.1 引用计数法:

给对象一个引用计数,当计数为0的时候,可以理解为,该对象不再被使用,可以释放内存。

但是这个方法很难解决一个问题:对象间的相互引用问题。

举个例子:

对象objA和objB都有字段instance。

objA.instance = objB;

objB.instance = objA;

它们没有其他引用。

但是它们的引用计数不可能为0.于是无法通知GC回收它们。

2.2 可达性分析算法:

主流的商用程序语言的主流实现中,都称为可达性分析。

这个算法的基本思想通过GC Roots的对象作为起始点。当一个对象,到起始点,没有连接的时候,可以理解为,这个节点的内存可以释放。

从图中可以看到,object5,object6,object7 这些对象会被GC回收。

2.3 引用

无论通过何种算法来实现GC,都和引用有关。

java1.2之后,引用分为 强引用,软应用,弱引用,已级虚引用。

强引用是java普遍存在的一种状态,类似new 一个对象。强引用不会被GC回收。

软引用,软引用是描述还有用,但未必要存在的对象。它的生存时间是,当内存不足时,也就是快要OOM的时候,GC会回收它。

弱引用,就是非必要的的对象。被弱引用指向的对象,只能生存到下一次GC之前。

虚引用,虚引用的目的是为了当GC发生时,可以收到一个系统通知。不会对引用对象产生任何影响。

2.4 对象如何判断要回收:

GC会对不可达的对象,做第一次标记。

当该对象执行finalize方法后,或者没有覆盖finalize方法,这回被第二次标记,这个时候,GC会被这段内存清理。

当对象执行finalize方法时,JVM会把它放在一个F-queue里面,这是这个对象实例拯救自己的最后机会。

它只需要在finalize里面,把this赋给某个变量。

任何一个对象finalize只会被执行一次。

3.垃圾收集的算法

3.1标记清楚算法:

顾名思义,分2端过程,先标记,再清除。先标记需要回收的内存,标记完成后,统一回收。

这个是最基础的算法,其他算法都是以此为基础。

2个缺点:效率问题,标记和清除效率都不高。内存碎片,标记清除之后会产生大量的空间碎片。如果申请大的内存

可能会申请不到,而不得不提前触发下一次GC

3.2 复制算法

就是将内存分为大小相等的2块,每次只使用一块,当一块用完的时候,就将还存活的部分,复制到另一块空间,然后

把已经使用的那块做一次性清理,这种清理速度非常快。

这样就不用考虑内存碎片的情况。只是这种算法代价就是一半的内存空间。

3.3 标记--整理算法

标记过程同前面一样,标记结束后,把内存块移到一起。(这里的移动是指针移动,逻辑内存移动,物理内存应该没有变化。)

让所有存活的对象移向一端,然后直接清理掉端边界以外的内存,清理速度很快,但是内存移动是耗时?。

从实现上说,清理应该是和硬盘删除文件一样,不是真正的删除,而是把对应的内存标记为 未使用。并且把文件名删除。

3.4 分代算法

根据对象存活周期的不同,分为新生代,和老年代。

在新生代,每次回收都有很多对象被回收,可以选用复制算法,只需要少量存活的对象复制成本就可以。复制成本地,并且发生概率高,清理速度快。

而老年代,存活的时间比较久,发生概率地,对空间要求高,对时间要求低。必须使用标记清楚或者标记整理方法。

现在商业虚拟机的配置,由于98%的对象可能会很快被释放,所以复制算法并非是1:1最合理。

HotSpot(Sun公司开发的虚拟机)分配就是一块较大的Eden空间& 2块Survivor空间。

比例位Eden:Survivor = 8:1.复制算法发生时,Eden+1Survivor 剩余的内存 copy到1Survivor上。

3.4.1 枚举根节点

在选择处理GC Roots方法时,GC Root所在的上下文可能有数百兆,所以在判断GC Roots的时候,应该确保一致性,JVM

应该停止所有java执行线程。所以GC是占用CPU作为代价!

4.内存分配策略和回收

java所解决的内存的管理归根到底就是2个问题,内存的分配 & 已分配内存的回收。关于回收,我们已经很详细的分析GC的算法。

4.1对象优先在Eden分配

大多数情况下,对象在新生代的Eden区中分配内存,当Eden区内存不足时,java虚拟机将发生GC,

释放不需要的对象。

4.2 大对象直接在老年代

大对象,是指需要大量连续空间的对象,比如很长的字串或者数组。

比大对象更糟糕的事情就是,遇到一群很快无用的 大对象。

大对象占据内存空间,尤其这些对象使用频率不高,这样就浪费了很多空间。容易照成GC的频率过多。

4.3 长期存活的对象将进入老年代

对象开始放在新生代eden区,如果度过一次GC,进入Survivor空间,对象年龄设置为1.

以后,每一次GC,年龄就加1。

当年龄到达一定的阀值以后(默认是15),就回进入老年代。

4.4 空间分配担保

为了充分利用新生代的内存空间,复制算法并没有1:1的来分配。所以有概率当Eden还保留大量的对象时,1个Survivor无法复制全部的对象

这个时候就需要部分内存放入老年代。But 如果老年代内存不足呢?那只能继续GC,以确保有足够的空间。

深入理解java虚拟机(2)------垃圾收集器和内存分配策略的更多相关文章

  1. 深入理解Java虚拟机:垃圾收集器与内存分配策略

    目录 3.2 对象已死吗 判断一个对象是否可被回收 引用类型 finalize() 回收方法区 3.3. 垃圾收集算法 1.Mark-Sweep(标记-清除)算法 2.Copying(复制)算法 3. ...

  2. 《深入理解Java虚拟机》——垃圾收集器与内存分配策略

    GC需要完成: 哪些内存需要回收 什么时候回收 如何回收 如何确定对象不再使用 引用计数算法 给对象添加一个引用计数器,当有一个地方引用它时,计数器值进行加1操作:当引用失效时,计数器值进行减1操作: ...

  3. 深入理解Java虚拟机笔记——垃圾收集器与内存分配策略

    目录 判断对象是否死亡 引用计数器算法 可达性分析算法 各种引用 回收方法区 垃圾收集算法 标记-清除算法 复制算法 标记-整理算法 分代收集算法 HotSpot算法实现 枚举根节点 GC停顿(Sto ...

  4. 深入理解java虚拟机(3)垃圾收集器与内存分配策略

    一.根搜索算法: (1)定义:通过一系列名为"GC Roots"的对象作为起点,从这些起点开始向下搜索,搜索走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连的时 ...

  5. 深入JAVA虚拟机笔记-垃圾收集器与内存分配策略

    第三章:垃圾收集器与内存分配 问题:1.哪些内存需要回收 2.什么时候回收 3.怎么回收 回收方法区:

  6. java虚拟机(六)--垃圾收集器和内存分配策略

    目前没有完美的收集器,不同的厂商.版本的虚拟机提供的垃圾收集器会有很大的差别,用户根据自己应用特点和要求组合出各个年代所使用 的收集器.基于jdk1.7Update14之后的虚拟机. HotSpot的 ...

  7. 深入理解Java虚拟机之读书笔记三 内存分配策略

    一般的内存分配是指堆上的分配,但也可能经过JIT编译后被拆散为标量类型并间接地在栈上分配.对象主要分配在新生代的Eden区上,如果启动了本地线程分配缓冲,将按线程优先在TLAB上分配,少数情况下直接分 ...

  8. 深入理解java虚拟机----->垃圾收集器与内存分配策略(下)

    1.  前言 内存分配与回收策略 JVM堆的结构分析(新生代.老年代.永久代) 对象优先在Eden分配 大对象直接进入老年代 长期存活的对象将进入老年代 动态对象年龄判定 空间分配担保  2.  垃圾 ...

  9. 《深入理解Java虚拟机》(三)垃圾收集器与内存分配策略

    垃圾收集器与内存分配策略 详解 3.1 概述 本文参考的是周志明的 <深入理解Java虚拟机>第三章 ,为了整理思路,简单记录一下,方便后期查阅. 3.2 对象已死吗 在垃圾收集器进行回收 ...

  10. 《深入理解java虚拟机》第三章 垃圾收集器与内存分配策略

    第三章 垃圾收集器与内存分配策略 3.1 概述 哪些内存需要回收 何时回收 如何回收 程序计数器.虚拟机栈.本地方法栈3个区域随线程而生灭. java堆和方法区的内存需要回收.   3.2 对象已死吗 ...

随机推荐

  1. 基于 Markdown 的开源的 Node.js 知识库平台

    Raneto 是一个免费,开源的 Node.js 知识库平台,基于静态 Markdown 文件实现. Raneto 可以被称为静态网站生成器,因为它并不需要数据库支持.所有的内容都存储在 Markdo ...

  2. "浅谈Android"第二篇:活动(Activity)

        距离上一篇文章,过去有半个多月了,在此期间忙于工作,疏于整理和总结,特此写下这篇博文,来谈谈自己对Activity的理解.总所周知,Activity组件在Android中的重要性不言而喻,我们 ...

  3. sql server 调用webservice

    sql server版本2008以上,应该都可以 更改服务器配置 sp_configure ; GO RECONFIGURE; GO sp_configure ; GO RECONFIGURE; GO ...

  4. BlocksKit初见:一个支持将delegate转换成block的Cocoa库

    简介 项目主页: https://github.com/zwaldowski/BlocksKit BlocksKit 是一个开源的框架,对 Cocoa 进行了扩展,将许多需要通过 delegate 调 ...

  5. C语言编译过程详解

    前言 C语言程序从源代码到二进制行程序都经历了那些过程?本文以Linux下C语言的编译过程为例,讲解C语言程序的编译过程. 编写hello world C程序: // hello.c #include ...

  6. 餐厅到店点餐系统(APP)

    MY-HR 成员: 角色分配 学号 博客园 丘惠敏 PM项目经理 201406114203 http://www.cnblogs.com/qiuhuimin/ 郭明茵 用户 201406114204 ...

  7. Winform开发框架之客户关系管理系统(CRM)的开发总结系列3-客户分类和配置管理实现

    我在本系列随笔的开始,介绍了CRM系统一个重要的客户分类的展示界面,其中包含了从字典中加载分类.从已有数据中加载分类.以及分组列表中加载分类等方式的实现,以及可以动态对这些节点进行配置,实现客户分类的 ...

  8. 重新想象 Windows 8 Store Apps (57) - 本地化和全球化

    [源码下载] 重新想象 Windows 8 Store Apps (57) - 本地化和全球化 作者:webabcd 介绍重新想象 Windows 8 Store Apps 之 本地化和全球化 本地化 ...

  9. 二、SQL语句映射文件(2)增删改查、参数、缓存

    //备注:该博客引自:http://limingnihao.iteye.com/blog/106076 2.2 select 一个select 元素非常简单.例如: Xml代码 收藏代码 <!- ...

  10. sessionId在fragment里无法保存的问题

    fragment页面需要验证用户是否登录.若没登陆调用登录页面,后返回fragment. 这个问题解决 sessionId = SharePreferenceUtils.getSessionId(); ...