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. [源码分析] Facebook如何训练超大模型---(1)

    [源码分析] Facebook如何训练超大模型---(1) 目录 [源码分析] Facebook如何训练超大模型---(1) 0x00 摘要 0x01 简介 1.1 FAIR & FSDP 1 ...

  2. Chrome插件:提醒你正在摸鱼,摸鱼的时候知道自己在摸鱼,减少摸鱼的时间和频率。

    stop-mess-around 项目介绍 减少摸鱼的时间和频率的Chrome插件:在上班/学习期间很容易下意识的打开摸鱼网站,插件帮助我们减少摸鱼的时间和频率,提高我们上班和学习的效率,节省时间用于 ...

  3. Redis作缓存

    缓存策略三要素:缓存命中率   缓存更新策略  最大缓存容量.衡量一个缓存方案的好坏标准是:缓存命中率.缓存命中率越高,缓存方法设计的越好. 三者之间的关系为:当缓存到达最大的缓存容量时,会触发缓存更 ...

  4. django_templates模板与html页

    新建应用 上一篇通过"django-admin startproject helloworld"是创建项目,一个项目下可以有多个应用(app).打开cmd,cd到manage.py ...

  5. 数组的sort()排序

    1.sort() 方法用于对数组的元素进行排序,并返回数组.默认排序顺序是根据字符串Unicode码点,也就是你不传参进去的话,默认按字符串Unicode码点来排序,而不是按数字大小来排序 2.arr ...

  6. 小程序循环时的item问题

    平常在做小程序时,比如循环渲染数据时,如果有多个数据层次,一般都会这样 wx:for-item=item2,它的意思只是简单的起了一个wx:for循环值的别名,不是表示循环item2,index2同理 ...

  7. gin中自定义路由日志的格式

    package main import ( "fmt" "github.com/gin-gonic/gin" "net/http" &quo ...

  8. 返回值String是文本数据

    MyController类中: index.jsp中 修改text前: 改为text后: 还是有乱码是因为使用这个ISO-8859-1编码处理的 MyController中修改注解中属性

  9. Qt中编译器

    很多时候,Qt构建项目编译的过程中会报错,大部分报错是因为qt的设置出现问题,很多时候环境配置时要选择合适的编译器,debugger调试器等,这里对一些名词解释,内容对新手很友好,大佬就不用看啦. M ...

  10. 用shell脚本写出检测/tmp/size.log文件,如果存在显示它的内容,不存在则创建一个文件将创建时间写入

    1 #!/bin/bash 2 if [ -d "/tmp" ]; then 3 echo "/tmp is exists" 4 else 5 mkdir /t ...