垃圾收集器与内存分配策略——深入理解Java虚拟机 笔记二
在本篇中,作者大量篇幅介绍了当时较为流行的垃圾回收器,但现在Java 14都发布了,垃圾收集器也是有了很大的进步和发展,因此在此就不再对垃圾收集器进行详细的研究。但其基本的算法思想还是值得我们参考学习的。
概述
第一篇笔记Java内存区域与内存溢出异常中讲到了,Java的内存划分可以分为由所有线程共享的Java堆和方法区,以及每个线程之间相互独立的程序计数器、本地方法栈、虚拟机栈。其中每个线程相互独立的部分,并不会给垃圾回收造成困难。因为随着方法、线程的结束,内存自然就释放了。因此我们垃圾收集关注的,都是Java堆和方法区这部分内存,因为这部分内存的回收和分配都是动态的。
对象存亡的判别
在对垃圾回收之前,我们应该先判断哪些对象没有用应该被回收、哪些对象应该被保留。
引用计数算法
引用计数算法:给对象中添加一个引用计数器,每当有一个地方引用此对象,计数器加1,当引用失效时,计数器减1,任何时候计数器为0的对象就是不可能再被使用的。
这种判别方法简单易行,但是Java语言并没有使用这样的方法来判别对象,主要的原因是这种算法无法解决对象之间相互调用的情况。
比如有这样的代码段:对象a和对象b之间相互引用,然后把他们全赋值为null,此时他们不可能再被访问,但是因为他们相互调用,引用计数器不为0,则无法释放对象。
public class TestGC {
public Object obj = null;
public static void main(String[] args) {
TestGC a = new TestGC();
TestGC b = new TestGC();
a.obj = b;
b.obj = a;
a = null;
b = null;
}
}
根搜索算法
为了解决上面提到的相互引用的问题,Java采用根搜索算法来判别对象是否存活。
根搜索算法的基本思路是:通过一系列名为"GC Roots"的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到"GC Roots"没有任何引用链相连的时候,则证明该对象是不可用的。
在Java中,可以作为"GC Roots"的对象包括:
1、虚拟机栈(栈帧中的本地变量表)中的引用的对象。
2、方法区中的类静态属性的引用对象。
3、方法区中的常量引用的对象。
4、本地方法栈中JNI(native方法)的引用对象。
对于如下的图,即使Object4和object5有链接关系,但是因为没有跟"GC Roots"相连,所以仍然认为是不可用的对象。
方法区中的判别
相对于堆中垃圾回收相比,方法区中的垃圾回收的性价比比较低,在堆中,尤其是在新生代中,常规垃圾回收一次可以回收70%~95%的空间。而永久代的垃圾收集效率远低于此。
永久代的垃圾回收主要回收两个部分:废弃常量和无用的类。
回收废弃常量很简单,只要判别没有对象引用此常量即可。而收集无用的类就要复杂许多。一般来说,类要满足如下三个条件才能看做无用的类:
1、该类的所有实例都已经被回收,也就是Java堆中不存在任何该类的实例。
2、加载该类的ClassLoader已经被回收。
3、该类对应的java.lang.class对象没有在任何地方被引用。无法在任何地方通过反射访问该类的方法。
在大量使用反射、动态代理的框架中,以及动态生成JSP的频繁自定义ClassLoader的场景,都需要虚拟机具有类卸载的功能,以保证永久代不会溢出。
垃圾收集算法
只是对于算法思路的讲解,而忽略具体的实现细节。
标记-清除算法
标记-清除算法是最基础的算法,它首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。
这个算法的好处是,实现简单,但是它有两个严重的问题:1、效率低下,标记和回收的效率都不高。2、空间问题,因为标记回收会造成大量的不连续的内存碎片,当一个较大的对象需要分配时,往往会因为找不到对应连续的存储空间而多次执行垃圾收集工作。
虽然标记-清除算法有这样的缺点,但其他的算法大多是在此算法的思路上进行优化,改进其存在的问题。
标记-清除算法的示意图如下:
复制算法
为了解决效率问题,复制算法将可用内存按容量划分为大小相等的两块,每次只使用其中的一块,当这一块内存用完了,就将还存活的对象复制到另外一块上面,然后把已使用过的内存空间全部清理。这样做的好处是不用考虑内存碎片问题,只要按顺序分配内存即可,而且每次只要对一块内存进行清理。但缺点就是使实际可用的内存空间减小。
复制算法的示意图如下:
但是,实际根据研究表明,新生代中98%的对象都是创建后存在很少一段时间就不可用了,因此不需要按照1:1的情况进行划分。因此这个算法的实际思路其实就是:进行一遍标记后,让还存活的保留下来,然后顺序安置到另一个区域,再重新使用该空间进行接下来变量的存储。
在作者写作时,表示商业虚拟机都采用此复制算法进行回收新生代。而内存划分为1个eden和2个survivor,每次使用eden和一个survivor来存储,另一个survivor来存储保存下来的对象。通常eden和survivor的大小比例是8:1.
但如果有超过10%的对象存活,就需要依赖其他内存(如老年代)来进行分配担保。
标记-整理算法
该算法与标记-清除算法相比,不同就是此算法会将仍然存在的对象往前移动,从而解决了大量内存碎片的问题。
其示意图为:
分代收集算法
该算法没有新的思想,只是按照对象存活周期将内存划分为几块,然后不同的内存划分对应于不同时期的对象,采用不同的垃圾收集算法。
内存分配与回收策略
内存分配有几条普遍的规则:
对象优先在Eden区分配。
大多数情况下,对象在新生代Eden区中分配,当Eden区没有足够空间进行分配的时候,虚拟机将发起一次Minor GC.
大对象直接进入老年代。
大对象就是需要大量连续内存空间的Java对象。像很长的字符串、数组之类。
长期存活的对象将进入老年代
虚拟机给每个对象定义了一个对象年龄计数器,对象每次在Minor GC下存活一次则加1.当达到阈值时,可以晋升为老年代。
动态对象年龄判定
并不是所有情况下,都必须要达到阈值才能晋升为老年代,如果Survivor中对象过多,也可以提前进入老年代。
空间分配担保
新生代使用复制算法时,并不能保证每次存活的对象都小于Survivor空间大小,如果超出了的话,就需要借用老年代的空间进行对象的存储。
垃圾收集器与内存分配策略——深入理解Java虚拟机 笔记二的更多相关文章
- 深入理解java虚拟机----->垃圾收集器与内存分配策略(下)
1. 前言 内存分配与回收策略 JVM堆的结构分析(新生代.老年代.永久代) 对象优先在Eden分配 大对象直接进入老年代 长期存活的对象将进入老年代 动态对象年龄判定 空间分配担保 2. 垃圾 ...
- 《深入理解Java虚拟机》(三)垃圾收集器与内存分配策略
垃圾收集器与内存分配策略 详解 3.1 概述 本文参考的是周志明的 <深入理解Java虚拟机>第三章 ,为了整理思路,简单记录一下,方便后期查阅. 3.2 对象已死吗 在垃圾收集器进行回收 ...
- 《深入理解java虚拟机》第三章 垃圾收集器与内存分配策略
第三章 垃圾收集器与内存分配策略 3.1 概述 哪些内存需要回收 何时回收 如何回收 程序计数器.虚拟机栈.本地方法栈3个区域随线程而生灭. java堆和方法区的内存需要回收. 3.2 对象已死吗 ...
- 深入理解java虚拟机_第三章(上)----->垃圾收集器与内存分配策略
1. 前言 这一版块内容比较多,分为两篇文章来做笔记.本文讲述上半部分垃圾收集部分;下一篇文章写内存分配部分. 概述 对象已死吗? 引用技术算法 可达性分析算法 再谈引用 两次标记 回收方法区 2. ...
- java虚拟机学习-JVM内存管理:深入垃圾收集器与内存分配策略(4)
Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外面的人想进去,墙里面的人却想出来. 概述: 说起垃圾收集(Garbage Collection,下文简称GC),大部分人都把这项 ...
- jvm系列 (二) ---垃圾收集器与内存分配策略
垃圾收集器与内存分配策略 前言:本文基于<深入java虚拟机>再加上个人的理解以及其他相关资料,对内容进行整理浓缩总结.本文中的图来自网络,感谢图的作者.如果有不正确的地方,欢迎指出. 目 ...
- 《深入java虚拟机》读书笔记之垃圾收集器与内存分配策略
前言 该读书笔记用于记录在学习<深入理解Java虚拟机--JVM高级特性与最佳实践>一书中的一些重要知识点,对其中的部分内容进行归纳,或者是对其中不明白的地方做一些注释.主要是方便之后进行 ...
- Java虚拟机垃圾收集器与内存分配策略
Java虚拟机垃圾收集器与内存分配策略 概述 那些内存须要回收,什么时候回收.怎样回收是GC须要完毕的3件事情. 程序计数器.虚拟机栈与本地方法栈这三个区域都是线程私有的,内存的分配与回收都具有确定性 ...
- JVM性能优化系列-(2) 垃圾收集器与内存分配策略
2. 垃圾收集器与内存分配策略 垃圾收集(Garbage Collection, GC)是JVM实现里非常重要的一环,JVM成熟的内存动态分配与回收技术使Java(当然还有其他运行在JVM上的语言,如 ...
随机推荐
- Java 集合框架总结--导图
java的集合导图总结:
- 新的知识点来了-ES6 Proxy代理 和 去银行存款有什么关系?
ES给开发者提供了一个新特性:Proxy,就是代理的意思.也就是我们这一节要介绍的知识点. 以前,ATM还没有那么流行的时候(暴露年纪),我们去银行存款或者取款的时候,需要在柜台前排队,等柜台工作人员 ...
- 详解数组分段和最大值最小问题(最小m段和问题)
数组分段和最大值最小问题(最小m段和问题) 问题描述 给定n个整数组成的序列,现在要求将序列分割为m段,每段子序列中的数在原序列中连续排列.如何分割才能使这m段子序列的和的最大值达到最小? 清洁工:假 ...
- thinkphp--导入导出xls文件
/** * 数组转xls格式的excel文件 * @param array $data 需要生成excel文件的数组 * @param string $filename 生成的excel文件名 * 示 ...
- [Qt] 去除窗体右上角的问号
this->setWindowFlags(windowFlags()&~Qt::WindowContextHelpButtonHint);
- 利用python画出动态高优先权优先调度
之前写过一个文章. 利用python画出SJF调度图 动态高度优先权优先调度 动态优先权调度算法,以就绪队列中各个进程的优先权作为进程调度的依据.各个进程的优先权在创建进程时所赋予,随着进程的推进或其 ...
- 使用spring boot创建fat jar APP
文章目录 介绍 build和run fat jar和 fat war 更多配置 介绍 在很久很很久以前,我们部署web程序的方式是怎么样的呢?配置好服务器,将自己写的应用程序打包成war包,扔进服务器 ...
- 在IBM Cloud中运行Fabric
文章目录 打包智能合约 创建IBM Cloud services 创建fabric网络 创建org和相应的节点 创建order org和相应节点 创建和加入channel 导入智能合约 上篇文章我们讲 ...
- 【Linux网络基础】TCP/IP 协议簇(各个常见协议介绍)
一.应用层协议 1. FTP 协议所在层次:应用层协议 名称:FTP协议 协议端口:20,21 协议说明: FTP(File Transfer Protocol,文件传输协议)是TCP/IP协议组 ...
- Spring IOC 之注册解析的 BeanDefinition
2019独角兽企业重金招聘Python工程师标准>>> DefaultBeanDefinitionDocumentReader.processBeanDefinition() 完成 ...