Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外面的人想进去,墙里面的人却想出来

垃圾收集概述

Java内存模型中的堆和方法区是垃圾收集技术所需要关注的终点,因为其他的区域会跟随线程的结束而自动回收。

而需要解决垃圾收集的首要目标便是解决如何判断一个对象已经不需要了从而自动进行回收;判断对象是否可以进行回收的算法可以分为引用计数算法可达性分析算法

对于Redis有一些了解的同学应该知道Redis的对象内存回收算法便是使用的引用计数算法;而JVM都是使用的可达性分析算法,在此我们只讨论可达性分析算法。

可达性分析算法

可达性分析算法简而言之便是从一些称为“GC Roots”的根对象作为起始节点集,根据引用关系向下搜索,搜索过的路径称为引用链,若某个对象到任何GC Roots对象都没有引用链,那么此对象便为不可达。

在Java技术体系中,固定作为GC Roots对象的包括以下几种:

  • 虚拟机栈中引用的变量(虚栈);
  • 方法区中类静态属性引用的对象(静);
  • 方法区中常量引用的对象(常);
  • 本地方法栈中引用的变量(本栈);
  • JVM虚拟机内部的引用(内);
  • 所有被同步锁持有的对象(锁);
  • 其他反映虚拟机内部情况的对象;

垃圾收集算法

从如何判定对象消亡的角度出发,垃圾收集算法可以划分为“引用计数式垃圾收集(直接垃圾收集)”与“追踪式垃圾收集(间接垃圾收集)”。

分代收集理论

分带收集理论是基于以下分代假说之上:

  • 弱分代假说:绝大多数对象都是朝生夕灭的;
  • 强分代假说:熬过越多次垃圾收集过程的对象就越难以消亡;
  • 跨代引用假说:跨代引用相对于同代引用来说仅占极少数;

以上的假说共同奠定了垃圾收集器的一致的设计原则:收集器应该将Java堆划分为不同的区域,然后根据回收对象的年龄分配到不同的区域中存储。

所以现代JVM垃圾收集器一般将Java堆划分为“新生代”与“老年代”;

针对不同分代的GC算法有以下几种:

  • 部分收集(Partial GC):不是完整收集整个Java堆的垃圾收集;

    • 新生代收集(Minor GC/Young GC):目标只是新生代的垃圾收集;
    • 老年代收集(Major GC/Old GC):目标只是老年代的垃圾收集,暂时只有CMS收集器实现了单独的老年代收集;
    • 混合收集(Mixed GC):目标是收集整个新生代以及部分老年代的垃圾收集,暂时只有G1收集器有这种行为;
  • 整堆收集(Full GC):收集整个Java堆和方法区的垃圾收集;

跨代引用假说主要是解决跨越“新生代”与“老年代”的对象之间引用的问题,根据该假说所衍生出的对于跨代引用的解决方法是:

在“新生代”上建立一个全局的数据结构(记忆集),该结构将老年代划分为若干小块,标识“老年代”中存在跨代引用的内存区域。此后发生Minor GC时,只有包含了跨代引用的小块内存里的对象才会假如GC Roots进行扫描。

垃圾收集算法

垃圾收集算法这里介绍三种:

  • 标记-清除算法
  • 标记-复制算法
  • 标记-整理算法

标记-清除算法

标记-清除算法是最早出现也是最基础的算法,其执行步骤为:

  1. 标记出需要回收的对象(标记);
  2. 统一回收掉所有已被标记的对象(清除);
  3. 或者第一步标记出存活的对象,第二步清理没有被标记的对象;

标记-清除算法的缺点如下:

  1. 执行效率不稳定,与需要被标记的对象数量相关;
  2. 内存碎片化问题,标记、清除之后会产生大量不连续的内存碎片,可能会导致后续如果需要分配较大对象时无法找到足够的连续对象从而触发另外一次GC;

标记-复制算法

标记-复制算法简称复制算法,解决的是标记-清除算法中面对大量可回收对象执行效率低的问题。

最早的标记-复制算法是一种称为“半区复制”的算法,其将可用内存按照容量大小划分为大小相等的两块,一块用以平时使用,另外一块用以GC时复制还存活的对象。

标记-复制算法是现在的商用Java虚拟机常用的算法,但是“半区复制”算法所浪费的内存过多达到了整个内存区域的一半,所以后续又进行了很多的改进;

Appel式回收

“Appel式回收”是一种更优化的半区复制分代策略,其将内存区域分为一块较大的Eden空间与两块较小的Survivor空间,每次分配内存的时候只使用一块Eden空间与一块Survivor空间;

“Appel式回收”的具体做法是:

存放对象的时候只会使用Eden空间与一块Survivor空间,然后垃圾收集的时候会将存活的对象移到另外一块未被使用的Survivor区域。

其实就相当于是将存活对象一直有一个区域可以存放,这样便避免了内存空间的浪费。

HotSpot虚拟机的Serial、Parnew等新生代收集器均采取了这种策略设计新生代的内存布局。

标记-整理算法

标记-整理算法是针对老年代对象的存亡特征所提出的有针对性的算法。标记步骤并没有进行改变,整理步骤时,是将所有存活的对象都向内存对象一端移动,然后直接清理掉边界以外的内存。

若移动存活对象,那么便在对象移动过程中必须全程暂停用户应用程序才能进行,被称为“Stop The World”。

若不移动对象,那么内存碎片化问题只能依赖更为复杂的内存分配器与内存访问器解决。

总结

以上便是内存分配策略与垃圾收集技术的理论基础,下一篇博客介绍现代JVM的实现细节。

JVM基础学习(二):内存分配策略与垃圾收集技术的更多相关文章

  1. JVM系列四(内存分配策略).

    一.概要 前面的文章介绍了对象的创建过程,其中第三步 -- 分配内存,只是简单的介绍了分配的方式 -- 指针碰撞.空闲列表,其实内存在堆上分配还大有文章嘞. 对象的内存分配,往大方向上讲,就是在堆上分 ...

  2. [jvm]垃圾回收与内存分配策略

    一.垃圾回收算法 概述 JVM中,当创建的对象不再被使用的时候,此时我们认为他是无用的“垃圾”:在现代主流的商用jvm中,都是通过可达性分析来判断对象是否存活的.这个算法的基本思想是通过一系列“GCR ...

  3. jvm垃圾回收器与内存分配策略

    一.判断对象存活的算法 1.引用计数算法 (1)概念:给对象中添加一个引用计数器每当有一个地方引用它时,计数器值加1:当引用失效时,计数器就减1:任何时刻计数器为0的对象就是不可能再被使用的. (2) ...

  4. JVM·垃圾收集器与内存分配策略之垃圾收集器!

    1.Serial(串行)收集器(新生代都采用复制算法)     这是个单线程的收集器:即 当他工作的时候,会停掉虚拟机所有的线程!(Stop The World)

  5. java虚拟机学习-JVM内存管理:深入垃圾收集器与内存分配策略(4)

    Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外面的人想进去,墙里面的人却想出来. 概述: 说起垃圾收集(Garbage Collection,下文简称GC),大部分人都把这项 ...

  6. jvm系列 (二) ---垃圾收集器与内存分配策略

    垃圾收集器与内存分配策略 前言:本文基于<深入java虚拟机>再加上个人的理解以及其他相关资料,对内容进行整理浓缩总结.本文中的图来自网络,感谢图的作者.如果有不正确的地方,欢迎指出. 目 ...

  7. JVM学习总结四——内存分配策略

    之前几篇我们介绍了jvm的内存模型以及垃圾回收机制,而本篇我们将介绍几个JVM中对象在分配内存是应该遵循的策略.毕竟,想要去优化程序,不仅要考虑垃圾回收的过程,还要从对象内存分配的角度减少gc的代价. ...

  8. JVM学习--内存分配策略(持续更新)

    一.前言 最近学习<深入java虚拟机>,目前看到内存分配策略这块.本文将进行一些实践. 二.内存分配策略 1.大对象直接进入老年代 书中提到了: 下面进行测试,代码如下: public ...

  9. JVM学习笔记-第三章-垃圾收集器与内存分配策略

    JVM学习笔记-第三章-垃圾收集器与内存分配策略 tips:对于3.4之前的章节可见博客:https://blog.csdn.net/sanhewuyang/article/details/95380 ...

随机推荐

  1. .NET Core 利用委托进行动态流程组装

    引言 在看.NET Core 源码的管道模型中间件(Middleware)部分,觉得这个流程组装,思路挺好的,于是就分享给大家.本次代码实现就直接我之前写的动态代理实现AOP的基础上直接改了,就不另起 ...

  2. 【Java】数组

    文章目录 数组 一.数组的定义 二.数组的声明与创建 三.内存分析 四.三种初始化 五.数组的四个基本特点 六.数组边界 七.数组的使用 八.多维数组 九.Arrays类 十.稀疏数组 数组 一.数组 ...

  3. Android中添加监听回调接口的方法

    在Android中,我们经常会添加一些监听回调的接口供别的类来回调,比如自定义一个PopupWindow,需要让new这个PopupWindow的Activity来监听PopupWindow中的一些组 ...

  4. tmux安装配置与使用

    tmux安装 sudo apt-get install tmux tmux配置 在家目录下操作 cd git clone https://github.com/gpakosz/.tmux.git ln ...

  5. 《剑指offer》面试题63. 股票的最大利润

    问题描述 假设把某股票的价格按照时间先后顺序存储在数组中,请问买卖该股票一次可能获得的最大利润是多少? 示例 1: 输入: [7,1,5,3,6,4] 输出: 5 解释: 在第 2 天(股票价格 = ...

  6. 网络编程-HTTP cookie

    目录 1.cookie的起源 2.cookie是什么? 3.创建cookie 3.1.响应首部 Set-Cookie 3.2.请求首部 Cookie 3.3.Document.cookie 4.HTT ...

  7. 【解决了一个小问题】golang protocol buffers 3中去掉json标签中的omitempty

    参考了这篇帖子:golang protobuf从生成的json标记中删除omitempty标记 由于是在windows上开发,因此写了一个python脚本来解决: remove_tag.py impo ...

  8. gin的源码解读4-gin的路由算法

    gin的路由算法 gin的是路由算法其实就是一个Trie树(也就是前缀树). 有关数据结构的可以自己去网上找相关资料查看. 注册路由预处理 我们在使用gin时通过下面的代码注册路由 普通注册 rout ...

  9. TreeMap相关

    Map接口 Map集合的特点 1.能够存储唯一的列的数据(唯一,不可重复) Set 2.能够存储可以重复的数据(可重复) List 3.值的顺序取决于键的顺序 4.键和值都是可以存储null元素的 T ...

  10. maven常用打包命令

    常用maven命令 执行与构建过程(编译,测试,打包)相关的命令必须进入pom.xml所在位置执行 mvn clean:清理(打包好的程序放在生成的名为target的文件中,清理即删除文件中打包好的程 ...