Java虚拟机(JVM):第三幕:自动内存管理 - 垃圾收集器与内存分配策略
前言:Java与C++之间有一堵高墙,主要是有内存动态分配和垃圾收集技术组成的。墙外的人想要进来,墙内的人想要出去。
一、概述
每一个栈帧中分配多少内存基本上是在类结构确定下来时就已知的。内存的分配和回收都具有确定性。
二、对象已死?
垃圾收集器在对堆进行回收之前,不能确定哪些“对象”活着,哪些“对象”死去。
1、引用计数算法
在对象中,添加一个引用计数器,当有一个地方引用它时,计数器的值加一;引用失效的时候,计数器的值减一;任何时刻,计数器为零的对象不能被再次使用。这样就存在一个问题,如果两个对象之间相互引用,是否永远不能够被回收。代码如下所示:
package com.example.dayevery;
public class ReferenceCountingGC{
public Object instance = null;
private static final int _1MB = 1024*1024;
// 测试 是否被回收
private byte[] bigSize = new byte[2 * _1MB];
public static void TestGc (){
ReferenceCountingGC objA = new ReferenceCountingGC();
ReferenceCountingGC objB = new ReferenceCountingGC();
objA.instance = objB;
objB.instance = objA;
objA = null;
objB = null;
// 测试 objA和objB是否被系统回收
System.gc();
}
public static void main(String[] args) {
// 提交执行
TestGc();
System.out.println("1");
}
}
通过结果发现,虚拟机最终并没有因为两个对象之间互相引用就放弃回收它们,也就是说Java虚拟机并不是通过引用技术算法来判断对象是否存活。
2、可达性分析算法
当前主流算法(Java.C#)的内存管理子系统,采用可达性分析算法来判断对象是否存活。
算法思路:通过一系列称之为“GC Roots”的根对象作为起始节点集,从这些节点开始,根据引用关系向下搜索,搜索过程走过的路径称之为“引用链”,如果某一个对象和GC Roots集合之间没有任何的引用链相连,则证明此对象之间不能被再次使用。
GC Roots:除了固定的集合之外,根据用户所选用的垃圾收集器以及当前回收的内存区域不同,还可能会有其他的“对象”临时的加入,从而构成了完整的GC Roots集合。
三、生存还是死亡?
当在可达性分析算法中,被标记为“死亡”的时候,该对象并不是真正的“死亡”,除了这一次的标记之外,还有第二次自救,那就是采用finalize()方法,从而让自己跟某一个起始节点集合挂上联系。
finalize()方法:能且只能执行一次,如果失败,那么就会被回收。这是一个被可以在Java语言中被遗忘的方法。
四、回收方法区
方法区回收的垃圾主要有两部分:废弃的常量和不再使用的类型。其中废弃的常量,举个例子,一个字符串“java”进入常量池中,没有任何的地方引用这个字符串,这属于废弃的常量。
不再使用的类型要满足以下几点。1、该类的所有实例被回收。2、该类的类加载器已经被全部回收。3、该类对应的java.lang.Class类对象没有在任何地方引用。
五、垃圾收集算法
垃圾收集算法可以划分为“引用计数式垃圾收集”和“追踪式垃圾收集”。本文主要介绍追踪式垃圾收集。
首先要介绍分代收集理论,它建立在两个分代假说之上:1、弱分代假说。2、强分代假说。此后,还延伸出来“跨代引用假说”:存在相互引用的两个对象应该倾向于同时生存或者同时消亡的。
针对不同的区域安排与里面存储对象存亡特征相匹配的垃圾收集算法:1、标记 - 复制算法。2、标记 - 消除算法。3、标记 - 整理算法。
1、标记 - 消除算法
标记 - 消除算法是最基础的算法,主要分为“标记”和“清除”两部分,标记过程就是对象是否属于垃圾的判定过程。
算法流程:首先标记出所有需要回收的对象,标记完成之后,统一回收掉所有被标记的对象。同时也存在两个缺点:1、执行效率不太稳定。2、内存空间的碎片化问题。
2、标记 - 复制算法
为了解决标记 - 清除算法,面对可回收对象时执行效率低的问题,提出采用“半区复制”,将内存划分为大小相等的两块,每次只是使用其中的一块。当其中一块内存用完之后,就将还存活的对象复制到另一块的上面,然后把已经使用过的内存空间一次性的清理到。最明显的缺点:可用内存缩小为原来的一半。
标记 - 复制算法中的“半区复制”在89年发生了变化,针对“新生代”朝生夕灭的特点,采用了一种更加优化的半区复制分代策略,称为Appel式回收:将新生代的内存分为一块较大(80%)的Eden空间和两块较小(10%)的Survivor空间。
每次使用Eden空间和一块Survivor空间,在垃圾回收的时候,将仍然存活的对象直接复制未被使用的Survivor空间中,当所需空间不能满足存活对象要求,采用“逃生门”的安全设计,依赖其他内存区域(老年代)进行分配担保。PS:有点像是银行借贷这种。
3、标记 - 整理算法
算法过程:标记 - 整理算法和标记 - 清除算法的第一步是相同的,但是标记 - 整理算法不能够直接对可回收对象进行清理,而是让所有的存活对象都往内存空间的一端移动,直接清理掉边界以外的内存。
六、HotSpot虚拟机的算法细节实现
见下一篇博客。
Java虚拟机(JVM):第三幕:自动内存管理 - 垃圾收集器与内存分配策略的更多相关文章
- 深入理解java虚拟机_第三章(上)----->垃圾收集器与内存分配策略
1. 前言 这一版块内容比较多,分为两篇文章来做笔记.本文讲述上半部分垃圾收集部分;下一篇文章写内存分配部分. 概述 对象已死吗? 引用技术算法 可达性分析算法 再谈引用 两次标记 回收方法区 2. ...
- Java虚拟机的内存管理----垃圾收集器
1.Serial收集器 优点,是简单而高效,单线程避免了线程交互的开销. 缺点,进行垃圾回收时需要Stop the world(暂停所有用户线程). 2.ParNew收集器 它是Serial收集器的多 ...
- JVM内存管理---垃圾收集器
说起垃圾收集(Garbage Collection,GC),大部分人都把这项技术当做Java语言的伴生产物.事实上,GC的历史远比Java久远,1960年诞生于MIT的Lisp是第一门真正使用内存动态 ...
- 《深入java虚拟机》读书笔记之垃圾收集器与内存分配策略
前言 该读书笔记用于记录在学习<深入理解Java虚拟机--JVM高级特性与最佳实践>一书中的一些重要知识点,对其中的部分内容进行归纳,或者是对其中不明白的地方做一些注释.主要是方便之后进行 ...
- 《深入理解java虚拟机》第三章 垃圾收集器与内存分配策略
第三章 垃圾收集器与内存分配策略 3.1 概述 哪些内存需要回收 何时回收 如何回收 程序计数器.虚拟机栈.本地方法栈3个区域随线程而生灭. java堆和方法区的内存需要回收. 3.2 对象已死吗 ...
- java虚拟机学习-JVM内存管理:深入垃圾收集器与内存分配策略(4)
Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外面的人想进去,墙里面的人却想出来. 概述: 说起垃圾收集(Garbage Collection,下文简称GC),大部分人都把这项 ...
- 《深入理解Java虚拟机》(三)垃圾收集器与内存分配策略
垃圾收集器与内存分配策略 详解 3.1 概述 本文参考的是周志明的 <深入理解Java虚拟机>第三章 ,为了整理思路,简单记录一下,方便后期查阅. 3.2 对象已死吗 在垃圾收集器进行回收 ...
- java虚拟机 jvm 出入java栈 栈空间内存分配
java栈空间是一块线程私有的内存空间,java堆和程序数据密切相关,那么java栈就是和线程执行密切相关.线程最基本的执行行为就是函数的调用.每次函数调用其实是通过java栈传递数据的. 数据结构中 ...
- 如何设置Java虚拟机JVM启动内存参数
Tomcat默认的Java虚拟机JVM启动内存参数大约只有64MB或者128MB,非常小,远远没有利用现在服务器的强大内存,所以要设置Java虚拟机JVM启动内存参数.具体设置方法为: Tomcat修 ...
- 【深入理解JAVA虚拟机】第二部分.内存自动管理机制.3.垃圾收集器与内存分配策略
1.学习目的 当需要排查各种内存溢出. 内存泄漏问题时,当垃圾收集成为系统达到更高并发量的瓶颈时,我们就需要对这些“自动化”的技术实施必要的监控和调节. Java内存运行时区域的各个部分,其中程序计数 ...
随机推荐
- 春秋杯春季联赛&&ciscn2023华北赛区部分题解
前言 复现几个比赛时没做出来的题 1.[CISCN 2023 华北赛区]ez_ruby 查文档可知 ruby内置的open函数,如果第一个字符是管道符|,后面就可以接命令.这可能是考察涉猎的知识范围广 ...
- ArrayList 扩容机制
ArrayList 基本介绍 ArrayList实现了List接口.它可以存储包括null的任何类型的对象,允许重复元素.ArrayList在内部使用一个数组来存储元素,当元素数量超过数组容量时,Ar ...
- SpringBoot定义优雅全局统一Restful API 响应框架完结撒花篇封装starter组件
之前我们已经,出了一些列文章. 讲解如何封统一全局响应Restful API. 感兴趣的可以看我前面几篇文章 (整个starter项目发展史) SpringBoot定义优雅全局统一Restful AP ...
- Redis缓存同步1-策略介绍
缓存数据同步策略示意图 在大多数情况下,我们通过浏览器查询到的数据都是缓存数据,如果缓存数据与数据库的数据存在较大差异的话,可能会产生比较严重的后果的.所以,我们应该也必须保证数据库数据.缓存数据的一 ...
- 【WebRtc】获取音视频数据
首页截图 获取音视频 关键Code 获取摄像头数据 /** * 获取流数据 */ openUserMeida() { var that = this // 判断是否支持获取媒体数据 if (!navi ...
- 防火墙(iptables与firewalld)
防火墙 iptables 疏通和堵 进行路由选择前处理的数据包:prerouting 处理流入的数据包:input 处理流出的数据包:output 处理转发的数据包:forward 进行路由选择后处理 ...
- Ubuntu16.04配置NTP时间同步
环境 查看系统版本:lsb_release -a 名词解释 PDT是指太平洋夏令时(Pacific Daylight Time),是美国西部地区和加拿大的一部分地区使用的时区.它位于UTC-7和UTC ...
- 智能制造之路—从0开始打造一套轻量级MOM平台之仓库管理(WMS)
一.前言 讲仓库管理(WMS)之前,我们先来谈一谈ERP.前一篇文章,大家可以看出,我在做MOM平台规划的时候并没有提到任何ERP的信息,并不是被忽略掉了:而是对于制造企业来说,ERP是重中之重. M ...
- K8S 对象
本页说明了在 Kubernetes API 中是如何表示 Kubernetes 对象的, 以及使用 .yaml 格式的文件表示 Kubernetes 对象. https://kubernetes.io ...
- 【go语言】1.1.2 Go 语言的特性
1. 简洁的语法 Go 语言的语法设计上非常简洁明了,没有复杂的继承和泛型,也没有异常处理,但这并不影响它的功能性和表达力.这使得 Go 语言容易学习和使用. 例如,以下是一个简单的 Go 函数,用于 ...