前言

在JVM内存模型中会将堆内存划分新生代、老年代两个区域,两块区域的主要区别在于新生代存放存活时间较短的对象,老年代存放存活时间较久的对象,除了存活时间不同外,还有垃圾回收策略的不同,在JVM中中有以下回收算法:

  • 标记清除
  • 标记整理
  • 复制算法
  • 分代收集算法

有了垃圾回收算法,那JVM是如果确定对象是垃圾对象的呢?判断对象是否存活JVM也会有几套自己判断算法了:

  • 引用记数
  • 可达性分析

有了垃圾回收和判断对象存在这两个概念后,再来逐步分析它们。

JVM是如何判断对象是否存活的?

要是让开发人员来判断一个对象是否有用是很简单的,简单的说就是:对象没有任何引用就认为该对象可以被回收了。假设有如下程序代码:

public class App {

    public static void main(){
checkFile("/");
} public static boolean checkFile(String path ){
File file = new File(path);
return file.exists();
}
}

程序执行起来在调用checkFile的时候JVM图大概像这样:

checkFile方法执行完成之后,它里面的局部变量file就会随着栈帧一起被清理,这个时候还存活在JVM堆中的File对象也是无用的了:

要是人为来判断非常清晰的就发现File对象已经无用了,那换成JVM它又是如何来判断对象是否能存活的呢?

引用记数

引用记数算法原理比较简单,想象下有个对象它有一个count属性,每次引用该对象都会使count加1,假设JVM在判断该对象是否存活的时候去检查这个count属性,发现这个属性不为0说明还有其他对象在引用该对象。

等到checkFile方法执行完之后count就会减1变成0:

这样一来JVM就很容易判断一个对象是否存活了。

但是引用记数有一个明显的缺点,就是无法解决循环引用的问题比如:A --> B --> A 这样的对象关系它是没有办法来判断对象是否该不该回收的。

GC Root(可达性分析)

为什么会被称为可达性分析算法呢?可以这样理解如果通过GC Root能到达一个对象那么这个对象就是存活的。那什么样的对象才是GC Root呢?

在Java语言中,可作为GC Roots的对象包括下面几种:

  1. 虚拟机栈中引用的对象(栈帧中的本地变量表);
  2. 方法区中类静态属性引用的对象;
  3. 方法区中常量引用的对象;
  4. 本地方法栈中JNI(Native方法)引用的对象。

还是用上面的例子,在checkFile方法执行时,因为栈帧变量file可做为GC Root所以在执行期间JVM是绝对不会回收掉这个File对象:

但是等到checkFile执行完成之后,这个栈帧会被弹出,其中的变量也会被释放,相应的没有GC Root能到达堆中的File对象,这个时候就可以判断这个对象是一个无用的对象了,然后安全回收。

垃圾收回算法

标记清除

这种算法分两分:标记、清除两个阶段,

标记阶段是从根集合(GC Root)开始扫描,每到达一个对象就会标记该对象为存活状态,清除阶段在扫描完成之后将没有标记的对象给清除掉。

用一张图说明:

这个算法有个缺陷就是会产生内存碎片,如上图B被清除掉后会留下一块内存区域,如果后面需要分配大的对象就会导致没有连续的内存可供使用。

标记整理

标记整理就没有内存碎片的问题了,也是从根集合(GC Root)开始扫描进行标记然后清除无用的对象,清除完成后它会整理内存。

这样内存就是连续的了,但是产生的另外一个问题是:每次都得移动对象,因此成本很高。

复制算法

复制算法会将JVM推分成二等分,如果堆设置的是1g,那使用复制算法的时候堆就会有被划分为两块区域各512m。给对象分配内存的时候总是使用其中的一块来分配,分配满了以后,GC就会进行标记,然后将存活的对象移动到另外一块空白的区域,然后清除掉所有没有存活的对象,这样重复的处理,始终就会有一块空白的区域没有被合理的利用到。

两块区域交替使用,最大问题就是会导致空间的浪费,现在堆内存的使用率只有50%。

分代回收

新生代回收

JVM的堆分为新生代和老年代,两种类型有不同的特性,根据它们的特性来选择不同的回收算法,这种算法会将新生代划分为一块Eden和二个Survivor区:

如上面的图有三块区域它们会按照8:1:1的比例进行分配,如1000m的堆Eden是800m,二个Survivor各占100m,那它们是如何运行的呢?

  1. 始终会有一块Survivor是空着的,内存使用率是90%
  2. 程序运行会在Eden和其中一块Survivor 1中分配内存
  3. 等到执行Minor gc,会将存活下来的对象移动到空着的Survivor 2
  4. 然后在EdenSurvivor 2中继续分配内存,Survivor 1空着等着下次使用

这样就能使内存使用率达到90%,也不会产生内存碎片。

老年代回收

老年代对象即使进行了垃圾回收,对象的存活率也高,所以采用标记清除或标记整理算法都是不错的选择,这里就不做阐述。



《架构文摘》每天一篇架构领域重磅好文,涉及一线互联网公司应用架构(高可用、高性 能、高稳定)、大数据、机器学习等各个热门领域。

JVM垃圾回收算法详解的更多相关文章

  1. Java中的垃圾回收算法详解

    一.前言   前段时间大致看了一下<深入理解Java虚拟机>这本书,对相关的基础知识有了一定的了解,准备写一写JVM的系列博客,这是第二篇.这篇博客就来谈一谈JVM中使用到的垃圾回收算法. ...

  2. 一张图看懂JVM之垃圾回收算法详解

    导读                                                                                                  ...

  3. JVM垃圾回收算法及回收器详解

    引言 本文主要讲述JVM中几种常见的垃圾回收算法和相关的垃圾回收器,以及常见的和GC相关的性能调优参数. GC Roots 我们先来了解一下在Java中是如何判断一个对象的生死的,有些语言比如Pyth ...

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

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

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

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

  6. GC垃圾回收机制详解

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

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

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

  8. JVM垃圾回收算法解析

    JVM垃圾回收算法解析 标记-清除算法 该算法为最基础的算法.它分为标记和清除两个阶段,首先标记出需要回收的对象,在标记结束后,统一回收.该算法存在两个问题:一是效率问题,标记和清除过程效率都不太高, ...

  9. JVM垃圾回收算法(最全)

    JVM垃圾回收算法(最全) 下面是JVM虚拟机运行时的内存模型: 1.方法区 Perm(永久代.非堆) 2.虚拟机栈 3.本地方法栈 (Native方法) 4.堆 5.程序计数器 1 首先的问题是:j ...

随机推荐

  1. 章节十六、8-ITestResult接口

    一.ITestResult:该接口就像一个监听器,能够监听每个方法执行后的状态(是否成功)并将结果返回给我们. package testclasses1; import org.testng.anno ...

  2. 那些年,我们误解的 JavaScript 闭包

    说到闭包,大部分的初始者,都是谈虎色变的.最近对闭包,有了自己的理解,就感觉.其实我们误解闭包.也被网上各种说的闭包的解释给搞迷糊. 一句话:要想理解一个东西还是看权威的东西. 下面我来通俗的讲解一个 ...

  3. SpringBoot系列__02HelloWorld探究

    在前文中,我们创建了一个简单的hello world,现在,利用这个简单的程序,来简单分析一下SpringBoot的启动过程. 如果你是使用过SSM框架的人,尤其是4.0之前的版本,相信你使用过xml ...

  4. 新手学习FFmpeg - 调用API计算关键帧渲染时间点

    通过简单的计算来,线上I帧在视频中出现的时间点. 完整代码请参考 https://andy-zhangtao.github.io/ffmpeg-examples/ 名词解释 首先需要明确以下名词概念: ...

  5. Redis的复制(Master/Slave)、主从复制、读写分离

    1.什么是Redis的复制 行话:也就是我们所说的主从复制,主数据更新后根据配置和策略自动同步到备用机的master/slave机制,Mater以写为主,slave以读为主. 2.能干什么 2.1.读 ...

  6. 关于W3Cschool定义的设计模式-常用的9种设计模式的介绍

    一.设计模式 tip:每种设计模式,其实都是为了更高效的,更方便的解决在面对对象编程中所遇到的问题. 什么是设计模式:     是一套经过反复使用.多人知晓的.经过分类的.代码设计经验的总结   为什 ...

  7. Python+OpenCV竖版古籍文字分割

    在做图片文字分割的时候,常用的方法有两种.一种是投影法,适用于排版工整,字间距行间距比较宽裕的图像:还有一种是用OpenCV的轮廓检测,适用于文字不规则排列的图像. 1. 思路 一开始想偷个懒,直接用 ...

  8. selenium退出语句区别

    selenium关闭窗口有两个方法,close与quit,我们稍作研究便知道这两个方法的区别. 1.看源码或API 这是close()的说明: Closes the current window. 关 ...

  9. hihttps教你在Wireshark中提取旁路https解密源码

    大家好,我是hihttps,专注SSL web安全研究,今天本文就是教大家怎样从wireshark源码中,提取旁路https解密的源码,非常值得学习和商业应用. 一.旁路https解密条件 众所周知, ...

  10. Spring MVC-从零开始-view-forward、redirect

    1.forward或redirect后,不再走viewResolver过程,直接重新从控制器开始 2.代码 package com.jt; import org.springframework.ste ...