前言: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):第三幕:自动内存管理 - 垃圾收集器与内存分配策略的更多相关文章

  1. 深入理解java虚拟机_第三章(上)----->垃圾收集器与内存分配策略

    1.  前言 这一版块内容比较多,分为两篇文章来做笔记.本文讲述上半部分垃圾收集部分;下一篇文章写内存分配部分. 概述 对象已死吗? 引用技术算法 可达性分析算法 再谈引用 两次标记 回收方法区 2. ...

  2. Java虚拟机的内存管理----垃圾收集器

    1.Serial收集器 优点,是简单而高效,单线程避免了线程交互的开销. 缺点,进行垃圾回收时需要Stop the world(暂停所有用户线程). 2.ParNew收集器 它是Serial收集器的多 ...

  3. JVM内存管理---垃圾收集器

    说起垃圾收集(Garbage Collection,GC),大部分人都把这项技术当做Java语言的伴生产物.事实上,GC的历史远比Java久远,1960年诞生于MIT的Lisp是第一门真正使用内存动态 ...

  4. 《深入java虚拟机》读书笔记之垃圾收集器与内存分配策略

    前言 该读书笔记用于记录在学习<深入理解Java虚拟机--JVM高级特性与最佳实践>一书中的一些重要知识点,对其中的部分内容进行归纳,或者是对其中不明白的地方做一些注释.主要是方便之后进行 ...

  5. 《深入理解java虚拟机》第三章 垃圾收集器与内存分配策略

    第三章 垃圾收集器与内存分配策略 3.1 概述 哪些内存需要回收 何时回收 如何回收 程序计数器.虚拟机栈.本地方法栈3个区域随线程而生灭. java堆和方法区的内存需要回收.   3.2 对象已死吗 ...

  6. java虚拟机学习-JVM内存管理:深入垃圾收集器与内存分配策略(4)

    Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外面的人想进去,墙里面的人却想出来. 概述: 说起垃圾收集(Garbage Collection,下文简称GC),大部分人都把这项 ...

  7. 《深入理解Java虚拟机》(三)垃圾收集器与内存分配策略

    垃圾收集器与内存分配策略 详解 3.1 概述 本文参考的是周志明的 <深入理解Java虚拟机>第三章 ,为了整理思路,简单记录一下,方便后期查阅. 3.2 对象已死吗 在垃圾收集器进行回收 ...

  8. java虚拟机 jvm 出入java栈 栈空间内存分配

    java栈空间是一块线程私有的内存空间,java堆和程序数据密切相关,那么java栈就是和线程执行密切相关.线程最基本的执行行为就是函数的调用.每次函数调用其实是通过java栈传递数据的. 数据结构中 ...

  9. 如何设置Java虚拟机JVM启动内存参数

    Tomcat默认的Java虚拟机JVM启动内存参数大约只有64MB或者128MB,非常小,远远没有利用现在服务器的强大内存,所以要设置Java虚拟机JVM启动内存参数.具体设置方法为: Tomcat修 ...

  10. 【深入理解JAVA虚拟机】第二部分.内存自动管理机制.3.垃圾收集器与内存分配策略

    1.学习目的 当需要排查各种内存溢出. 内存泄漏问题时,当垃圾收集成为系统达到更高并发量的瓶颈时,我们就需要对这些“自动化”的技术实施必要的监控和调节. Java内存运行时区域的各个部分,其中程序计数 ...

随机推荐

  1. Python运维开发之路《python基础介绍》

    一. python介绍相关 1. Python简介 Python 是一个高层次的结合了解释性.编译性.互动性和面向对象的脚本语言. - Python 的设计具有很强的可读性,相比其他语言经常使用英文关 ...

  2. 数仓性能调优:大宽表关联MERGE性能优化

    摘要:本文主要为大家讲解在数仓性能调优过程中,关于大宽表关联MERGE性能优化过程. 本文分享自华为云社区<GaussDB(DWS)性能调优:大宽表关联MERGE性能优化>,作者:譡里个檔 ...

  3. SpringIoc容器之Aware

    1 前言 Aware是Spring提供的一个标记超接口,指示bean有资格通过回调样式的方法由Spring容器通知特定的框架对象,以获取到容器中特有对象的实例的方法之一.实际的方法签名由各个子接口确定 ...

  4. 前端Vue自定义开屏启动广告组件,点击广告图跳转广告详情

    随着技术的发展,开发的复杂度也越来越高,传统开发方式将一个系统做成了整块应用,经常出现的情况就是一个小小的改动或者一个小功能的增加可能会引起整体逻辑的修改,造成牵一发而动全身. 通过组件化开发,可以有 ...

  5. MySQL5.5+配置主从同步并结合ThinkPHP5设置分布式数据库

    前言: 本文章是在同处局域网内的两台windows电脑,且MySQL是5.5以上版本下进行的一主多从同步配置,并且使用的是集成环境工具PHPStudy为例.最后就是ThinkPHP5的分布式的连接,读 ...

  6. 快速掌握Vue3:速成Vue3前端开发看这篇就够啦

    一.Vue基本概念 1.1-Vue3的优点 Vue3支持Vue2额大多数特性. 更好的支持TypeScript. 打包大小减少41%. 初次渲染快55%,更新渲染快133%. 内存减少54%. 使用p ...

  7. JDK中「SPI」原理分析

    目录 一.SPI简介 1.概念 2.入门案例 2.1 定义接口 2.2 两个实现类 2.3 配置文件 2.4 测试代码 二.原理分析 1.ServiceLoader结构 2.iterator迭代方法 ...

  8. 使用TypeScript类型注解,编写更干净的JS代码

    TypeScript 可以看作是 JavaScript 的超集,不仅包含了 JavaScript 的所有内容,还拓展了语法.规定了类型约束,使得我们可以编写更干净.完整的代码. 类型注解 TypeSc ...

  9. [git]记配置本地git到gitlab并推送

    前言 gitlab仓库地址:git@192.168.0.12:godev/gohello.git 步骤 # 配置用户 git config --global user.name "zhang ...

  10. Hugging News #0807: ChatUI 官方 Docker 模板发布、🤗 Hub 和开源生态介绍视频来啦!

    每一周,我们的同事都会向社区的成员们发布一些关于 Hugging Face 相关的更新,包括我们的产品和平台更新.社区活动.学习资源和内容更新.开源库和模型更新等,我们将其称之为「Hugging Ne ...