深入浅出JVM内存模型+垃圾回收算法

前言

博主在寒假的时间,学车的空余时间又学了学周志明老师的深入理解Java虚拟机,结合之前的写下了这篇笔记,希望和大家一块学习,同时巩固自己记忆!

JVM内存模型

众所周知,jvm分五个模块:
虚拟机栈、本地方法栈、程序计数器、方法区、和堆。
其中五大内存模块可分为两种:线程共享和线程私有
线程共享: 虚拟机栈、程序计数器、本地方法栈
线程私有: 方法区、堆
图示:
在这里插入图片描述

1. 程序计数器(记录当前线程)

程序计数器是一块很小的内存空间,它是线程私有的,可以认作为当前 线程的行号指示器(类似于CPU里的PC)
对于一个处理器(如果是多核cpu那就是一核),在一个确定的时刻都只会执行一 条线程中的指令,一条线程中有多个指令,为了线程切换可以恢复到正确执行位 置,每个线程都需有独立的一个程序计数器,不同线程之间的程序计数器互不影 响,独立存储。 注意:如果线程执行的是个java方法,那么计数器记录虚拟机字节码指令的地 址。如果为native【底层方法】,那么计数器为空。而且这块内存区域是虚拟机规范 中唯一没有OutOfMemoryError的区域。

2. Java栈(虚拟机栈)

同计数器也为线程私有,生命周期与线程相同,就是我们平时说的栈,描述的是 Java方法执行的内存模型。 每个方法被执行的时候都会创建一个栈帧用于存储局部变量表,操作栈,动态链 接,方法出口等信息。每一个方法被调用的过程就对应一个栈帧在虚拟机栈中从 入栈到出栈的过程。虚拟机栈中的局部变量表中还存放了编译期的可知的八个基本数据类型,这里也是一个小的考点哦:
整型: byte,short,int,long 浮点型: float,double 字符型:char boolean型:boolean
以及对象的引用和returnAddress类型
在这里插入图片描述
这里要再提一点就是:对象访问的方式

  1. 指针直接访问实例数据
    在这种方式中,JVM栈中的栈帧中的本地变量表中所存储的引用地址就是实例数据的地址。通过这个引用就能直接获取到实例数据的地址。
    除此之外,其实引用所指向的对内存中的对象数据有两部分组成,一部分就是这个对象实例本身,另一部分是对象类型在方法区中的地址。
  2. 使用句柄间接访问实例数据
    JVM会在堆中划分一块内存来作为句柄池,JVM栈中的栈帧中的本地变量表中所存储的引用地址**是这个对象所对应的句柄地址,而非对象本身的地址。**句柄池中的一个个对象地址有两部分组成,一部分就是对象数据在堆内存中实例池中的地址,另一部分就是对象类型在方法区中的地址。

在这里插入图片描述

3. 本地方法栈

本地方法栈是与虚拟机栈发挥的作用十分相似,区别是虚拟机栈执行的是Java方 法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的native方法服务,这里个人理解的是:前者调用外部的代码,本地方法栈调用的是JDK自带的代码。本地方法栈可能底层调用的c或者c++,我们打开jdk安装目录可以看到也有很多用c编写的文件,可能就是native方法所调用的c代码。

4. 堆

周老师在这里皮了一下,说这个是 “垃圾堆” 这个比喻很形象,所谓垃圾堆即指GC频繁发生的区域,即对象多的地方。在这个区域所有线程是共享的,它的目的是存放对象实例。同时它也是GC所管理的主要区域,因此常被称为GC堆(“垃圾堆”),又由于现在收集器常使用分代算法,Java堆中还可以 细分为新生代和老年代,再细致点还有Eden(伊甸园)空间等等,如下图所示。 根据虚拟机规范,Java堆可以存在物理上不连续的内存空间,就像磁盘空间只要 逻辑是连续的即可。它的内存大小可以设为固定大小,也可以扩展。 当前主流的虚拟机如HotPot都能按扩展实现(通过设置 -Xmx和-Xms),如果堆中没有内存内存完成实例分配,而且堆无法扩展将报OOM错误 (OutOfMemoryError)
图示:
在这里插入图片描述

5.方法区

方法区同堆一样,是所有线程共享的内存区域,为了区分堆,又被称为非堆。 用于存储已被虚拟机加载的版本、字段、方法、接口和常量池(存储字面量和符号引用)、常量、静态变量,如 static 修饰的变量加载类的时候就被加载到方法区中。这里就解释了之前的博客里面的

String name=new String("HXZ");

强引用类型。
为什么就创建一或者两个对象了:
1 个的情况: 如果字符串池中已经存在了"HXZ"这个对象,那么直接在创建一个对象放入堆中,返回 name 引用。
2 个的情况: 如果字符串池中未找到"HXZ"这个对象,那么分别在堆中和字符串池中创建一个对象,但是如果遇到了new关键字,则还是会在内存(不是字符串池)中创建一个对象,然后将对象返回给引用name。字符串池中的比较都是采用equals方法。String的equals的方法是重写了Object超类13个方法中的equals的方法故能进行字符串内容的比较,重写equals方法必须重写hashcode方法(why?比如我去酒店,登记的前台好比哈希算法,名字是对比好比 equals 方法,身份证号的对比好比 hashcode 方法只有equals 和 hashcode 都满足的时候才能确保是同一个对象)。
这里还是强调一点,就是在jdk1.8及以后方法区的字符串池被挪到了堆里,不在方法区了,方法区的永久代也被元空间所取代。

6.直接内存

直接内存并不是虚拟机规范定义的数据区的一部分,也不是虚拟机运行时数据区 的一部分。但是这部分内存也被频繁的使用,而且也可能导致 OutOfMemoryerror。
调用native方法时,如要操作native方法的数据时,需要来回不停地复制,而直 接内存可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆 中的DirectByteBuffer对象作为这块内存的引用进行操作。这样能在一些场合中 显著提高性能,因为避免了在Java堆和Native堆中来回复制数据。

JVM垃圾回收

前面讲到堆是Java虚拟机是虚拟机所管理的内存里面最大的一块,所有的对象几乎都在这里分配内存。《深入理解Java虚拟机》这本书里还提到了随着时间的推移,这种情况也在随之改变:JIT编译器 的发展和
针对不同区域JVM采用了不同的GC,不同的GC是通过不同的算法实现的。在JDK8中,按照回收区域的不 同,把GC分为工作在新生代的普通GC(minorGC)和工作在堆全局空间的全局GC(Full GC)。 新生代和老年代占比空间为1:2,且采用了不同的算法,所以minor GC 的速度要比Full GC快很多。

在这里插入图片描述

垃圾判断标准

1. 引用计数法

给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻计数器为0的对象就是不可能再被使用的。
目前主流的java虚拟机都摒弃掉了这种算法,最主要的原因是它很难解决对象
之间相互循环引用的问题。尽管该算法执行效率很高。

2. 可达性分析算法(根索法)

这个算法的基本思路就是通过一系列的称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连(用图论的话来说,就是从GC Roots到这个对象不可达)时,则证明此对象是不可用的。
在这里插入图片描述

圾回收算法

1. 标记清除

先假设堆中所有对象都可以回收,然后找出不能回收的对象,给这些对象打上标记,最后堆中没有打标记的对象都是可以被回收的。这种算法虽然不需要多余的内存空间 “周转” 对象,但是会导致内存碎片化,如图所示:
在这里插入图片描述

2. 复制算法

HotSpot 把新生代分为三个部分:Eden区和两个Survivor区(From区和To区),(上方有图,这里不放了)默认比例8:1:1。对象 创建时会被放在Eden区,当Eden区触发GC(minor GC),GC会对Eden和Survivor区进行垃圾回收,将Eden和Survivor中还存活着的对象一次性复制到另外一块Survivor空间上,最后清理掉Eden和刚才用过的Survivor空间。而幸存下来的对象会被 “复制” 到Survivor1区(To区),然后清空Eden和From区,最后将To和From 交换,让刚才被清空的From作新的To区,让刚才保存对象的To区作新的From区,以保证下一次GC可以扫 描到这些对象。这个过程中涉及到了一个 “复制” 的操作,就是 “复制算法” 的产物。顺带一提:当一个对 象在多次GC后依然无法被回收,在From区和To区来回复制,每复制一次“年龄”加1,一旦“年龄”达到 MaxTenuringThreshold的值(阈值,默认为15)就会被移动到老年代。
如图所示:
在这里插入图片描述
“复制算法” 的优缺点:
优点:1、由于“复制算法”采用了复制—清空的方法,所以不会导致内存空间的碎片化。
缺点:1、由于复制算法需要另外的空间来 “周转” 这些幸存的对象,所以内存消耗比较大。2、如果存在“极端情况”,比如大量的对象循环引用而导致无法回收的幸存对象占比很大,假设 为80%,那么就需要将这些数量庞大的对象都复制遍,并将所有的引用地址重置一遍,这回耗费比较多 的时间。所以复制算法的最佳工作环境就是这一块的对象存活率比较低。

3. 标记整理

标记整理其实就是在标记清除后加了一个 “整理” 操作,将分散的数据整理到一块连续的内存空间。就是慢,但慢工出细活。
在这里插入图片描述
优点:减少了地址空间的占用。
缺点:时间效率问题。

4.分代回收

一般是把Java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。而老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用“标记—清除”或者“标记—整理”算法来进行回收

GC垃圾回收器

如图所示:
在这里插入图片描述
按照新生代和老年代的划分,可将GC回收器分成两部分,

1. Serial 垃圾收集器(单线程、复制算法) (新生代)

Serial(英文连续)是最基本垃圾收集器,使用复制算法,曾经是JDK1.3.1之前新生代唯一的垃圾 收集器。Serial 是一个单线程的收集器,它不但只会使用一个 CPU 或一条线程去完成垃圾收集工 作,并且在进行垃圾收集的同时,必须暂停其他所有的工作线程,直到垃圾收集结束。 Serial 垃圾收集器虽然在收集垃圾过程中需要暂停所有其他的工作线程,但是它简单高效,对于限 定单个 CPU 环境来说,没有线程交互的开销,可以获得最高的单线程垃圾收集效率,因此 Serial 垃圾收集器依然是java虚拟机运行在Client模式下默认的新生代垃圾收集器。

2. ParNew 垃圾收集器(Serial+多线程) (新生代)

ParNew垃圾收集器其实是Serial收集器的多线程版本,也使用复制算法,除了使用多线程进行垃 圾收集之外,其余的行为和Serial收集器完全一样,ParNew垃圾收集器在垃圾收集过程中同样也 要暂停所有其他的工作线程。
ParNew 收集器默认开启和 CPU 数目相同的线程数,可以通过-XX:ParallelGCThreads 参数来限 制垃圾收集器的线程数。【Parallel:平行的】 ParNew虽然是除了多线程外和Serial收集器几乎完全一样,但是ParNew垃圾收集器是很多java 虚拟机运行在Server模式下新生代的默认垃圾收集器。

3. Parallel Scavenge 收集器(多线程复制算法、高效) (新生代)

Parallel Scavenge 收集器也是一个新生代垃圾收集器,同样使用复制算法,也是一个多线程的垃 圾收集器,它重点关注的是程序达到一个可控制的吞吐量(Thoughput,CPU 用于运行用户代码 的时间/CPU 总消耗时间,即吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)), 高吞吐量可以最高效率地利用 CPU 时间,尽快地完成程序的运算任务,主要适用于在后台运算而 不需要太多交互的任务。自适应调节策略也是 ParallelScavenge 收集器与 ParNew 收集器的一个 重要区别。

4. Serial Old 收集器(单线程标记整理算法 ) (老年代)

Serial Old 是 Serial 垃圾收集器年老代版本,它同样是个单线程的收集器,使用标记-整理算法, 这个收集器也主要是运行在Client默认的java虚拟机默认的年老代垃圾收集器。 在Server模式下,主要有两个用途:

  1. 在JDK1.5之前版本中与新生代的Parallel Scavenge收集器搭配使用。
  2. 作为年老代中使用CMS收集器的后备垃圾收集方案。 新生代Serial与年老代Serial Old搭配垃圾收集过程图:
    在这里插入图片描述

5. Parallel Old 收集器(多线程标记整理算法)(老年代)

Parallel Old收集器是Parallel Scavenge的年老代版本,使用多线程的标记-整理算法,在JDK1.6 才开始提供。 在 JDK1.6 之前,新生代使用 ParallelScavenge 收集器只能搭配年老代的 Serial Old 收集器,只 能保证新生代的吞吐量优先,无法保证整体的吞吐量,Parallel Old 正是为了在年老代同样提供吞 吐量优先的垃圾收集器,如果系统对吞吐量要求比较高,可以优先考虑新生代 Parallel Scavenge 和年老代Parallel Old收集器的搭配策略。 新生代Parallel Scavenge和年老代Parallel Old收集器搭配运行过程图:
在这里插入图片描述

6. CMS 收集器(多线程标记清除算法) (老年代)

参考

周志明《深入理解Java虚拟机》
https://www.jianshu.com/p/2f4a8e04c657
https://blog.csdn.net/canot/article/details/51037938
https://www.jianshu.com/p/d09207c047ec

发布了9 篇原创文章 · 获赞 73 · 访问量 8734

猜你喜欢

转载自blog.csdn.net/AAAhxz/article/details/103969259