一文看懂javaGC
javaGC回收机制
在面试java后端开发的时候一般都会问到java的自动回收机制(GC)。在了解java的GC回收机制之前,我们得先了解下Java虚拟机的内存区域。
java虚拟机运行时数据区
java虚拟机在执行的过程中会将其管理的内存划分为不用的数据区域,不同的区域有不同的作用以及线程时间。
数据区划分如下:

下面将介绍不同区域的作用,如果已经了解可以跳过
程序计数器(线程私有)
程序计数器的作用很简单,就是记录当前线程所执行的位置(所以为线程私有),可以看成当前线程所执行的字节码的行号指示器。如果执行的是native方法,则这个计数器为空。
Java虚拟机栈(线程私有,生命周期与线程相同)
虚拟机栈描述的是Java方法执行的内存模型:每个Java方法在执行的时候都会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。
本地方法栈(线程共享)
本地方法栈与虚拟机栈发挥的作用类似,不过它执行的是虚拟机使用的Native方法。
Java堆(线程共享)
Java堆是Java虚拟机管理内存中最大的一块,在虚拟机启动的时候创建。此区域的唯一目的就是存放对象示例,几乎所有的对象实例都是在这分配内存的。
方法区(线程共享)
刚开始的时候,看到方法区域,第一想法就是
Java中的方法
,不过实际上并不是这样。方法区储存的是已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据。我们可以想一想,当我们需要创建一个对象的时候,我们需要根据类的信息去创建,那么类的信息在哪?当然是在方法区!运行时常量池
运行时常量池是方法区的一部分,用于存放编译期生成的各种字面量和符号引用。
垃圾收集(Garbage Collection)GC
前面说了这么多,现在我们终于可以来说说垃圾回收机制了。
首先我们得说下垃圾回收回收的是哪一部分内存区域。在前面我们知道:程序计数器,虚拟机栈,本地方法栈都是线程私有的,随着线程生或灭。这部分我们就不需要考虑了。所以我们需要考虑的就是Java堆
和方法区
。
垃圾回收的内容
回收java堆
对象是否可以被回收
判断对象是否被回收就是当一个对象死了的时候就需要进行回收。那么如何判断一个对象是否死亡,在Java中,我们使用了可达性分析算法来判断对象是否存活。
当一个对象到GC Roots没有任何链(称为
引用链
)相连(也就是对象到GC Roots不可达)则判定对象已经死亡(如图中的Object5,Object6),可进行回收。可作为GC Roots的对象:
- 虚拟机栈(栈帧中的本地变量表)中引用的对象
- 方法区中类静态属性引用的对象
- 方法区中常量引用的对象
- 本地方法栈中JNI(即一般说的Native方法)引用的对象
在前面中,我们知道,不可达就意味着回收,可是当我们的内存很够时,有一些对象又是“食之无味弃之可惜”的时候,我们怎么办呢?在JDK1.2中,Java对引用进行扩张,分为以下引用:
- 强引用(Strong Reference):只要强引用还在,则不回收
- 软引用(Soft Reference):描述一些有用但非必须的对象,在系统将要发生内存溢出之前,将这些对象列入回收范围之中进行第二次回收。<java.lang.ref.SoftReference>
- 弱引用(Weak Reference):比软引用还要弱,被弱引用关联的对象只能生存到下一次垃圾收集发生之前。<java.lang.ref.WeakReference>
- 虚引用(Phantom Reference):不会对生存时间构成影响,唯一的作用就是这个对象被回收的时候会收到一个通知。<java.lang.ref.PhantomReference>
最终判断对象是否能够存活
在可达性分析算法中,如果一个对象不可达,那么这个对象就进入到了“缓刑”阶段,真正宣告一个对象死亡还需要进行两次标记。
第一次标记进行筛选
对不可达的对象进行第一次标记并进行筛选。筛选的条件是此对象是否有必要执行finalize()方法。当对象没有覆盖finalize方法,或者finzlize方法已经被虚拟机调用过(意思就是finalize()方法只能被调用一次,也就是对象只能够有一次避免被回收),虚拟机将这两种情况都视为“没有必要执行”,对象被回收。
第二次标记
如果这个对象被判定为有必要执行finalize()方法,那么这个对象将会被放置在一个名为:F-Queue的队列之中,并在稍后由一条虚拟机自动建立的、低优先级的Finalizer线程去执行。这里所谓的“执行”是指虚拟机会触发这个方法,但并不承诺会等待它运行结束。这样做的原因是,如果一个对象finalize()方法中执行缓慢,或者发生死循环(更极端的情况),将很可能会导致F-Queue队列中的其他对象永久处于等待状态,甚至导致整个内存回收系统崩溃。
finalize()方法是对象脱逃死亡命运的最后一次机会,稍后GC将对F-Queue中的对象进行第二次小规模标记,如果对象要在finalize()中成功拯救自己----只要重新与引用链上的任何的一个对象建立关联即可,譬如把自己赋值给某个类变量或对象的成员变量,那在第二次标记时它将移除出“即将回收”的集合。如果对象这时候还没逃脱,那基本上它就真的被回收了。
回收方法区
在Java虚拟机规范中说过不要求方法区实现垃圾收集,并且进行垃圾收集的“性价比”也较低。不过既然写了,那必定有方法区的垃圾收集,主要回收以下两部分内容:
废弃常量:字面量和符号引用
无用的类:
- 该类的所有实例都被回收,即:Java堆中不存在该类的任何实例
- 该类的Classloader已经被回收
- 该类对用的java.lang.Class对象没有任何地方被引用,无法在任何地方通过反射访问到该类的方法。
当满足以上三个条件时,也未必说是一定要被回收。也仅仅是可以。
垃圾收集算法
年代划分
我们通过对象的存活周期来将JVM堆中内存空间划分为新生代和老年代。
新生代:主要是用来存放新生的对象。一般占据堆的1/3空间。
老年代:主要存放应用程序中生命周期长的内存对象。
算法
OK,说了这么多,我们现在终于可以来说说垃圾收集的算法了。
下面的图片来源于这位大佬,这位大佬讲的真滴不错。
标记-清除算法(Mark-Sweep)
标记:首先标记需要回收的对象,标记完成统一回收
清除:就是清除对象,释放空间
缺点:标记和清除的效率不高,同时产生大量不连续的内存碎片(可能不利于下次的空间分配)。
标记整理法
标记整理算法相比较于标记清除算法,标记-整理算法在清除的时候并不是一个一个的清除对象释放空间,而是一次清除全部的可回收的空间。这样使得空间变得连续,有利于对象空间的分配。
复制算法
- 将内存分成两块大小相等的空间。
- 每次使用其中一块。
- 进行垃圾回收的时候,将不要的回收的对象复制到另外一个空间
- 完全清除原来的空间。
优点:速度快,效率高,不会产生内存碎片。
缺点:显而易见,空间浪费大,缩小了一半。
解决方法:
IBM研究表明:新生代98%的对象是“朝生夕死”,所以我们并不需要将空间划分为1:1,而是将空间划分为
Eden:Survivor:Survivor = 8:1:1
。每次使用Eden和其中一块Survivor。- 使用其中Eden和一块Survivor。
- 进行回收时,讲Eden和Survivor还存活的对象一次性的复制到另外一块Survivor上。
- 清理第一步中的Eden和Survivor。
如果第二步中Survivor的空间不足,则依赖于其他内存(老年代)进行分配担保(也就是讲存活的对象放入老年代)。
分代收集算法
分代收集算法其实就是前面几种算法的应用。根据年代使用不同的算法
- 新生代GC(MinorGC,回收速度快):复制算法
- 老年代(Full GC/Major GC,比Minor慢10倍以上):标记整理法和标记清除法。
对象分配内存区域
- 新生代:大多数情况下爱,对象在新生代Eden区中分配。如果没有足够的空间,则发起一次MinorGC。
- 老年代:
- 大对象直接进入老年代。比如说很长的字符串或数组。
- 长期存活的对象:没熬过一次MinorGC,年龄age增加一岁,当它的年龄超过一定岁数时(默认15,可设置),则进入老年代中。
- 动态对象年龄判定:如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代。
参考书籍:《深入理解Java虚拟机》——周志明,这本书写的太好了,写的通熟易懂。强烈推荐去看看。
一文看懂javaGC的更多相关文章
- 一文看懂web服务器、应用服务器、web容器、反向代理服务器区别与联系
我们知道,不同肤色的人外貌差别很大,而双胞胎的辨识很难.有意思的是Web服务器/Web容器/Web应用程序服务器/反向代理有点像四胞胎,在网络上经常一起出现.本文将带读者对这四个相似概念如何区分. 1 ...
- 一文看懂https如何保证数据传输的安全性的【转载、收藏】
一文看懂https如何保证数据传输的安全性的 一文看懂https如何保证数据传输的安全性的 大家都知道,在客户端与服务器数据传输的过程中,http协议的传输是不安全的,也就是一般情况下http是明 ...
- [转帖]一文看懂web服务器、应用服务器、web容器、反向代理服务器区别与联系
一文看懂web服务器.应用服务器.web容器.反向代理服务器区别与联系 https://www.cnblogs.com/vipyoumay/p/7455431.html 我们知道,不同肤色的人外貌差别 ...
- [转帖] 一文看懂:"边缘计算"究竟是什么?为何潜力无限?
一文看懂:"边缘计算"究竟是什么?为何潜力无限? 转载cnbeta 云计算 雾计算 边缘计算... 知名创投调研机构CB Insights撰文详述了边缘计算的发展和应用前景 ...
- 一文看懂Stacking!(含Python代码)
一文看懂Stacking!(含Python代码) https://mp.weixin.qq.com/s/faQNTGgBZdZyyZscdhjwUQ
- Nature 为引,一文看懂个体化肿瘤疫苗前世今生
进入2017年,当红辣子鸡PD-1疗法,一路横扫多个适应症.而CAR-T治疗的“小车”在获得FDA专委会推荐后也已经走上高速路,成为免疫治疗又一里程碑事件.PD-1.CAR-T之后,下一个免疫治疗产品 ...
- 一文看懂大数据的技术生态圈,Hadoop,hive,spark都有了
一文看懂大数据的技术生态圈,Hadoop,hive,spark都有了 转载: 大数据本身是个很宽泛的概念,Hadoop生态圈(或者泛生态圈)基本上都是为了处理超过单机尺度的数据处理而诞生的.你可以把它 ...
- 转载来自朱小厮博客的 一文看懂Kafka消息格式的演变
转载来自朱小厮博客的 一文看懂Kafka消息格式的演变 ✎摘要 对于一个成熟的消息中间件而言,消息格式不仅关系到功能维度的扩展,还牵涉到性能维度的优化.随着Kafka的迅猛发展,其消息格式也在 ...
- 【转帖】一文看懂docker容器技术架构及其中的各个模块
一文看懂docker容器技术架构及其中的各个模块 原创 波波说运维 2019-09-29 00:01:00 https://www.toutiao.com/a6740234030798602763/ ...
随机推荐
- 使用JScript编译指定目录下所有工程
作者:朱金灿 来源:http://blog.csdn.net/clever101 我遇到这样一个问题:在一个插件工程目录下的插件工程越来越多,因此通过建一个解决方案然后把新增加的工程逐个添加进解决方案 ...
- 简单使用.net core 自带的DI
1.创建一个web api项目 2.在项目中创建一个接口类 namespace LearnCore.CoreDI { public interface ILearnDI { string GetNam ...
- 随机森林与 GBDT
随机森林(random forest),GBDT(Gradient Boosting Decision Tree),前者中的森林,与后者中的 Boosting 都在说明,两种模型其实都是一种集成学习( ...
- uow Unit of work
通过学习圣杰的文章 UnitOfWork知多少 知道uow其实就是为了解决 一次提交所有更改 1.ef本身可以具备这样一个功能,但是我们在写仓储的实现的时候 经常会直接显式saveChanges了 ...
- opengl编程指南 第七版 源代码bug Page35 lines.c 红宝书
问题1:根据源代码时,我发现的时候去敲门.不正确实施效果.哪里是不正确?没有源代码glPushAttrib(GL_LINE_STIPPLE) glPopAttrib().所以会出现最后的下一次抽奖提供 ...
- Java之java.lang.IllegalMonitorStateException
今天又中彩了, 原本很简单的多线程程序, 蓦然间冒了个"java.lang.IllegalMonitorStateException" , 杀了个措手不及. 一直纳闷, 为什么为什 ...
- C# WPF 中用代码模拟鼠标和键盘的操作
原文:C# WPF 中用代码模拟鼠标和键盘的操作 原文地址 C#开发者都知道,在Winform开发中,SendKeys类提供的方法是很实用的.但是可惜的是,在WPF中不能使用这个方法了. 我们知道,在 ...
- ControlTemplate
ControlTemplate:外观定制 <Window.Resources> <ControlTemplate x:Key="CheckBoxControlTemplat ...
- 读BeautifulSoup官方文档之html树的搜索(2)
除了find()和find_all(), 这里还提供了许多类似的方法我就细讲了, 参数和用法都差不多, 最后四个是next, previous是以.next/previous_element()来说的 ...
- .NET CORE EnvironmentVariable
.NET CORE System variables SETIn System variablese.g1:Variable name: ASPNETCORE_ENVIRONMENTVariable ...