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. centos nfs配置--转载

    http://www.centos.org/docs/5/html/Deployment_Guide-en-US/s1-nfs-client-config.html 18.6. NFS Server ...

  2. 【转载】让windows使用linux系统的命令

    转载自:http://blog.chinaunix.net/uid-28336510-id-3824903.html 1.下载GnuWin32,GnuWin32的安装文件(GetGnuWin32-0. ...

  3. Ember Charts – 基于 Ember & D3 的图表库

    Ember Charts 是一个基于 Ember.js 和 D3.js 的图表库.它包括时间序列.柱状图.饼图.点图,很容易扩展和修改.这些图表组件代表图表交互性和演示的最佳实践,是高度可定制和可扩展 ...

  4. Android学习笔记之Drawable 文件夹

    PS:最近闲着无聊..模仿去写个QQapp..效果还不错..并且从中又学习到了一些相关的东西,在这里进行一些相关总结.. 学习内容: Android 中 Drawable 文件夹内部相关属性..   ...

  5. redis主从遇到的两个坑

    最近在使用redis主从的时候做了下面两件事情: 1 希望redis主从从操作上分析,所有写操作都在master上写,所有读操作都在从上读. 2 由于redis的从是放在本地的,所以有的key的读写操 ...

  6. struts2基础——请求与响应、获取web资源

    一.请求与响应 Action1.含义:(1) struts.xml 中的 action 元素,也指 from 表单的 action 属性,总之代表一个 struts2 请求.(2) 用于处理 Stru ...

  7. 四项技术 助你提高SQL Server的性能

    有时,为了让应用程序运行得更快,所做的全部工作就是在这里或那里做一些很小调整.但关键在于确定如何进行调整!迟早您会遇到这种情况:应用程序中的 SQL 查询不能按照您想要的方式进行响应.它要么不返回数据 ...

  8. git 删除错误提交的commit

    方法: 根据–soft –mixed –hard,会对working tree和index和HEAD进行重置:    git reset --mixed:此为默认方式,不带任何参数的git reset ...

  9. 自定义View_1_关于View,ViewGroup的测量和绘制流程

    自定义View(1) ------ 关于View,ViewGroup的测量和绘制流程 在Android当中,自定义控件属于比较高级的知识体系,今天我们就一起研究研究关于自定义View的那点事,看看它到 ...

  10. iOS 线程相关-----绝对de干货

    平时用线程总是知其然,而不知所以然,现在针对涉及到的有关线程的知识体系做了一个系统的整理,由于GCD平时用的也比较多,所以用了大量的空间来讲述这一块,其他的涉及的不是很多,也做了说明,真真切切的是一个 ...