深入理解JAVA虚拟机《二》
对象、内存回收和垃圾收集算法
一、引用计数算法(不可靠)
- 现在很多比较普遍的判断对象是否存活的算法就是引用计数算法,其大概原理是:给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1,;任何时刻计数器为0的对象就是不可能再使用。
- 目前很多公司都有用到应用这个算法,客观地说,引用计数算法实现简单,判定效率也高,大多数情况下都是一个不错的算法。但是,至少主流的Java虚拟机里面没有选用引用计数算法来管理内存,最主要的原因就是它很难解决对象之间相互循环引用的问题。 举个例子,对象objA 和 objB 都有字段 instance,赋值令 objA.instance = objB; 及 objB.instance = objA; 除此之外,这两个对象再无任何引用,实际上这两个对象已经不可能再被访问,但是他们之间因为互相引用着对方,导致它们的引用计数都不为0,所有引用计算法无法通知GC收集器回收他们。
二、可达性分析算法
- 目前的主流商用程序语言(Java、C#等)的主流实现中,都是称通过可达性分析来判断对象是否存活的。这个算法的基本思想就是:通过一系列的称为 “GC Roots” 的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到 GC Roots 没有任何引用的时链相连的时候(这个时候,就是 GC Roots 到这个对象不可达),则可证明此对象是不可用的,即可通知 GC 收集器进行回收。 如下图所示,对象object 5、object 6、object7 虽然互相有关联,但是它们到 GC Roots是不可达的,所有被判定为可回收对象。
三、引用
在 JDK 1.2 之后,Java对引用的概念进行了补充,将引用分为 强引用、软引用、弱引用、虚引用这4种引用,这4种引用强度逐渐减弱。
- 强引用:程序代码之中普遍存在的,类似 “Object obj = new Object() ” 这类的引用,只要强引用还存在,垃圾回收器永远不会回收被引用的对象。
- 软引用:用来描述一些还有用但并非必需的对象。对于软引用关联着的对象,在系统将要发生OOM异常之前,将会把这些对象列进回收范围之中进行第二次回收。通俗地讲,内存足够,不进行回收,内存不足,进行回收。在 JDK 1.2 之中,提供了 SoftReference 类来实现软引用。
- 弱引用:用来描述非必需的对象,强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾回收收集发生之前。当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。一旦被扫描到,就会被回收。在 JDK 1.2 之中,提供了 WeakReference 类来实现弱引用。
- 虚引用:最弱的一种引用关系,一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。随时随刻,都会被回收。为一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知。在 JDK 1.2 之中,提供了 PhantomReference 类来实现虚引用。
四、对象的死亡
在可达性分析算法判定了一个对象不可达即被回收的时候,还不能判定该对象死亡;要真正宣告一个对象死亡,至少要经历两次标记过程:如果对象在进行可达性分析后发现没有与GC Roots 相连接的引用链,那它将会第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行 finalize() 方法。当对象没有覆盖finalize() 方法,或finalize() 方法已经被虚拟机调用过,虚拟机将这两种情况都视为“没有必要执行”。 如果这个对象被判定为有必要执行 finalize() 方法,那么这个对象将会放置在一个叫做 F - Queue的队列之中,并在稍后由一个由虚拟机自动建立的、低优先级的 Finalizer 线程去执行它。
五、回收方法区
永久代的垃圾收集主要回收两部分内容:废弃常量和无用的类。回收废弃常量和回收Java堆中的对象非常相似。以常量池中字面量的回收为例,假如一个字符串 “abc” 已经进入了常量池中,但是当前系统没有任何一个 String 对象是叫做 “abc” 的,换句话说,就是没有任何 String 对象引用常量池中的 “abc” ,也没有其他地方引用了这个字面量,如果这时发生内存回收,而且必要的话,这个“abc”常量就会被系统清理出常量池。常量池中的其他类(接口)、方法、字段的符号引用也与此类似。
类需要同时满足下面3个条件才能算是“ 无用的类 ”:
- 该类所有的实例都已经被回收,也就是Java堆中不存在该类的任何实例。
- 加载该类的ClassLoader已经被回收。
- 该类对应的java.lang.Class 对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
七、垃圾收集算法
标记 - 清除算法
算法分为 “标记” 和 “清除” 两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。它的主要不足有两个:一个是效率问题,标记和清除两个过程的效率都不高;另一个是空间问题,标记清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致以后在程序运行过程中需要分配较大对象时,无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。
复制算法
将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。这样使得每次都是对整个半区进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况,只要移动堆顶指针,按顺序分配内存即可,实现简单,运行高效。缺点是将内存缩小成原来的一半,未免也太高了一点。
现在的商业虚拟机都采用复制算法来回收新生代,IBM公司的专门研究表明,新生代中的对象 98% 是 “朝生夕死”的,所有并不需要按照 1:1 的比例来划分内存空间,而是将内存分为一块较大的 Eden 空间和两个较小的 Survivor空间,每次使用 Eden 和其他一块 Survivor。当回收时,将 Eden 和 Survivor 中还存活着的对象一次性地复制到另外一块 Survivor 空间上,最后清理掉 Eden 和刚才用过的 Survivor空间。HotSpot虚拟机默认 Eden 和 Survivor 的大小比例是 8:1 ,也就是每次新生代中可用内存空间为整个新生代容量的 90% ,只有 10%的内存会被浪费。
标记 - 整理算法
首先标记出所有需要回收的对象,让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。
未完善,待更新…
深入理解JAVA虚拟机《二》的更多相关文章
- 《深入理解Java虚拟机》虚拟机性能监控与故障处理工具
上节学习回顾 从课本章节划分,<垃圾收集器>和<内存分配策略>这两篇随笔同属一章节,主要是从理论+实验的手段来讲解JVM的内存处理机制.好让我们对JVM运行机制有一个良好的概念 ...
- 《深入理解 java虚拟机》学习笔记
java内存区域详解 以下内容参考自<深入理解 java虚拟机 JVM高级特性与最佳实践>,其中图片大多取自网络与本书,以供学习和参考.
- (1) 深入理解Java虚拟机到底是什么?
好文转载:http://blog.csdn.net/zhangjg_blog/article/details/20380971 什么是Java虚拟机 作为一个Java程序员,我们每天都在写Java ...
- 深入理解java虚拟机(7)---线程安全 & 锁优化
关于线程安全的话题,足可以使用一本书来讲解这些东西.<Java Concurrency in Practice> 就是讲解这些的,在这里 主要还是分析JVM中关于线程安全这块的内容. 1. ...
- 深入理解java虚拟机(6)---内存模型与线程 & Volatile
其实关于线程的使用,之前已经写过博客讲解过这部分的内容: http://www.cnblogs.com/deman/category/621531.html JVM里面关于多线程的部分,主要是多线程是 ...
- 深入理解java虚拟机(5)---字节码执行引擎
字节码是什么东西? 以下是百度的解释: 字节码(Byte-code)是一种包含执行程序.由一序列 op 代码/数据对组成的二进制文件.字节码是一种中间码,它比机器码更抽象. 它经常被看作是包含一个执行 ...
- 深入理解java虚拟机(4)---类加载机制
类加载的过程包括: 加载class到内存,数据校验,转换和解析,初始化,使用using和卸载unloading过程. 除了解析阶段,其他过程的顺序是固定的.解析可以放在初始化之后,目的就是为了支持动态 ...
- 深入理解java虚拟机(1)------内存区域与内存溢出
在C++领域,关于C++的内存存储,结构等等,有一本书:深度探索C++对象模型,讲解的非常透彻. 而Java确把这一工作交给了虚拟机来处理. 我们首先来看看关于内存的问题. 1.问题: 1)java ...
- 什么是HotSpot VM & 深入理解Java虚拟机
参考 http://book.2cto.com/201306/25434.html 另外,这篇文章也是从一个系列中得出的: <深入理解Java虚拟机:JVM高级特性与最佳实践(第2版)> ...
- 【Todo】深入理解Java虚拟机 读书笔记
有一个在线系列地址 <深入理解Java虚拟机:JVM高级特性与最佳实践(第2版)> http://book.2cto.com/201306/25426.html 已经下载了这本书(60多M ...
随机推荐
- uwp 之语音识别
xml code ---------------------------------------------- <Page x:Class="MyApp.MainPage" ...
- C++ 中的信号的处理
C++ 信号处理 信号是由操作系统传给进程的中断,会提早终止一个程序.在 UNIX.LINUX.Mac OS X 或 Windows 系统上,可以通过按 Ctrl+C 产生中断. 有些信号不能被程序捕 ...
- ffmpeg 视频ts切片生成m3u8
下面几种转换方式是不同版本和方法 新版本ffmpeg转视频直接可以切边并生成 m3u8(目前用的方式,也可以用选项 segment ): ffmpeg -i '源文件.mp4' -c:v h264 - ...
- 图解Java 垃圾回收机制
摘要: Java技术体系中所提倡的 自动内存管理 最终可以归结为自动化地解决了两个问题:给对象分配内存 以及 回收分配给对象的内存,而且这两个问题针对的内存区域就是Java内存模型中的 堆区.关于对象 ...
- Flink API
一.Flink API 1.DataSet:对静态数据进行批处理操作.将静态数据抽象成分布式数据集,使用Flink各种操作符处理数据,支持 Java .Scala.Python 2.DataStrea ...
- 有关Java动态数组的一个小问题
前言 问题描述 今天遇到一个关于集合的问题,觉得比较有趣,记录一下,这个问题是:定义一个用户类,至少包含姓名,年龄,生日,qq邮箱,初始化10个用户,利用String操作,提取qq到List集合中,姓 ...
- 接口测试进阶接口脚本使用--apipost(预/后执行脚本)
预执行脚本的作用时间 预执行脚本是一个请求发送前执行的脚本. 预执行脚本的作用 预执行脚本可以完成以下作用: 编写JS函数等实现复杂计算: 变量的打印 定义.获取.删除.清空环境变量 定义.获取.删除 ...
- C# - 音乐小闹钟_BetaV3.0
时间:2017-11-22 作者:byzqy 介绍: 音乐小闹钟 BetaV3.0 新鲜出炉了,快来围观吧!上效果图: 是不是觉得顿时变得高大上了许多呢?^_^ 工具/原料: (操作系统:Window ...
- MySQL-库表操作详述
一.库操作 创建库 create database 库名(charset utf8 对库的编码进行设置,不写就用默认值) 库名可以由字母.数字.下划线.特殊字符,要区分大小写,唯一性,不能使用关键字, ...
- MySQL-SQL基础-查询1
#子查询-某些情况下,当进行查询的时候,需要的条件是另外一个select语句的结果,这个时候就要用到子查询.用于子查询的关键字主要包括: in.not in.=.!=.exists.not exist ...