JVM运行时数据区和垃圾回收机制

简介: 最近参考各种资料,尤其是《深入理解Java虚拟机 JVM高级特性和最佳实践》,大牛之作。把最近学习的Java虚拟机组成和垃圾回收机制总结一下。

最近参考各种资料,尤其是《深入理解Java虚拟机 JVM高级特性和最佳实践》,大牛之作。把最近学习的Java虚拟机组成和垃圾回收机制总结一下。

你不会的都是新知识,学无止境,每天进步一点点。

一、认识Java虚拟机

在开始学Java之时,必做的一件事就是从Java官网下载并安装Java到我们的电脑之上,然后从HelloWorld开始走上编程的不归路。
这里写图片描述

上图中下载的Java安装包全称是Java SE Development Kit(单词依次翻译:Java 标准版本 开发 工具包),简称JDK,也就是供程序员使用的Java开发工具包。另外,JDK一般都是和它的2个小弟一起出现的,一个是JRE,一个是JVM,它们的关系如下图所示:
这里写图片描述

JDK=JRE+Java编译器、开发工具和更多的类库

简单来说JDK支持Java程序的开发。

JRE是Java Runtime Environment的简称

JRE=Java虚拟机+Java基础类库

是Java程序运行所需要的软件环境,简单的来说JRE支持Java程序的运行。图片中也提到了,JRE只支持java字节码的运行,是没办法把Java代码编译成.class文件的。

最内部也是最核心的就是Java虚拟机了,那么问题来了,什么是Java虚拟机?

Java虚拟机实际上是一种规范,是一种抽象机器,依附在真实的操作系统之上,这个规范描述了一个指令集,一组寄存器,一个堆栈,一个“垃圾堆”,和一个方法区。一旦一个Java虚拟机在给定的平台上运行,任何Java程序都能在这个平台上运行。

二、JVM运行时数据区

编写好的java代码交给java虚拟机,java虚拟机在执行java程序的过程中会把它所管理的内存划分为若干个不同的数据区域,称为运行时数据区,如下图所示。

这里写图片描述

图中绿色标记的区域是每个线程私有的,也就是线程安全的。灰色标记的区域是所有线程共享的,非线程安全。这幅图中我们只需要关注运行时数据区中的5个部分。

2.1 程序计数器

程序计数器的功能:

当前线程所执行的字节码的行号指示器。

.java文件编译成.class文件,最终交给jvm执行,.class文件也是要按逻辑找到行号执行的,程序计数器就是对当前线程应该执行哪一行字节码做指示的。假设线程A暂停,过一段时间后线程A继续执行,这时候线程A应该从哪个地方继续就是靠程序计数器来完成的。

每个线程一个程序计数器,不同线程之间的程序计数器互不影响。

2.2 虚拟机栈

虚拟机栈描述的是java方法执行的内存模型。

一个线程一个栈,一个方法一个栈帧。栈帧可以理解成栈中的一小块,一个栈中有多个栈帧。栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。
如下,MethodStackTest类中有三个方法:methodA、methodB和methodC,methodA调用了methodB,methodB调用了methodC,在main方法中执行methodA。

这里写图片描述

代码及打印结果:

class MethodStackTest{
    public static void methodA(){

        methodB();
        System.out.println("Method A");
    }
    public static void methodB(){
        methodC();
        System.out.println("Method B");
    }
    public static void methodC(){
        System.out.println("Method C");
        //methodA();取消注释会出现栈内存溢出
    }
   public static void main(String[] args) {
      methodA();    
   }
}
结果:

Method C
Method B
Method A

这个小例子有助于解释了栈的含义,A调用了B,B调用了C,只有C执行结束,B才会结束,最终B执行完成A才会结束。

另外,如果在C中再调用A的话就一直循环调用,超过虚拟机所允许的栈的深度以后就会抛出StackOverflowError异常,如果不能申请到足够的内存,就会抛出OutOfMemory异常。

2.3 本地方法栈

本地方法栈和虚拟机栈发挥的作用相似,本地方法栈为本地方法服务。

2.4 堆

Java 中的堆是 JVM 所管理的最大的一块内存空间,主要用于存放各种类的实例对象。堆被划分成三个不同的区域:新生代 ( Young )、老年代 ( Old )和永久区。新生代 ( Young ) 又被划分为三个区域:Eden、From Survivor、To Survivor。Java堆也是垃圾回收的主战场,也被称为GC堆。

堆大小=新生代+老年代+永久区


新生代=Eden+From Survivor+To Survivor

默认:Eden:From:To=8:1:1

这里写图片描述

另外新生代和老年代的比例可以通过参数设置,并不一定是1:1。

2.5 方法区

方法区也被称为永久区,里面有类信息、常量、静态变量等数据,别名为非堆,为各个线程所共享。

三、垃圾回收机制

3.1 什么是垃圾?

Java中的垃圾是指已经分配内存但不再有任何引用(不完全准确,后面会再说)的对象,垃圾回收(GC)就是自动清理这部分内存。

3.2 两种垃圾判断算法

3.2.1 引用计数法

给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻计数器为0的对象就是不可能再被使用的。

这种算法的有点事实现简单,判定效率也高,但是主流虚拟机并没有采用,主要是对于循环引用的对象无法进行回收。

3.2.3 可达性分析法

主流的JVM回收垃圾主要采用可达性分析法,算法的核心思想是选定一系列GC ROOTS对象作为起始点,从节点开始向下搜索,搜索路径称为引用链。一个对象到GC ROOTS没有任何引用链证明对象不可用。
这里写图片描述

可作为GC ROOTS的对象:

  1. 虚拟机栈中引用的对象
  2. 方法区中静态属性引用的对象
  3. 方法区中常量引用的对象
  4. 本地方法中JNI引用的对象

3.3 四种垃圾回收算法

3.3.1 标记-清除法(Marked-Sweep)

标记清除算法是最基础的收集算法,其他收集算法都是基于这种思想。标记清除算法分为“标记”和“清除”两个阶段:首先标记出需要回收的对象,标记完成之后统一清除对象。

它的主要缺点:

  1. 标记和清除过程效率不高 。
  2. 标记清除之后会产生大量不连续的内存碎片。

这里写图片描述

3.3.2 复制算法(Copying)

它将可用内存容量划分为大小相等的两块,每次只使用其中的一块。当这一块用完之后,就将还存活的对象复制到另外一块上面,然后在把已使用过的内存空间一次理掉。这样使得每次都是对其中的一块进行内存回收,不会产生碎片等情况,只要移动堆订的指针,按顺序分配内存即可,实现简单,运行高效。

主要缺点:内存缩小为原来的一半。

这里写图片描述

3.3.3 标记-整理法

标记操作和“标记-清除”算法一致,后续操作不只是直接清理对象,而是在清理无用对象完成后让所有存活的对象都向一端移动,并更新引用其对象的指针。

主要缺点:在标记-清除的基础上还需进行对象的移动,成本相对较高,好处则是不会产生内存碎片。

这里写图片描述

3.3.4 分代算法

分代算法就是根据堆内存中新生代和老年代对象的特点,不同的区域采用不同的垃圾回收算法。新生代大多数对象“朝生夕死”,每次都有大量对象被回收,因此采用复制算法,上图堆内存中FROM和TO就是为了复制算法而设计的。老年代对象存活率较高,没有额外空间进行分配担保,使用“标记-清理”或者“标记整理”算法。

3.4 七个垃圾收集器

七种作用于不同分代的垃圾收集器:

这里写图片描述
垃圾收集器这部分没有写的太详细,这里有一篇已经整理好的:http://www.jianshu.com/p/50d5c88b272d,其内容也是根据《深入理解Java虚拟机 JVM高级特性和最佳实践》整理的。

3.4.1 Serial收集器

这里写图片描述

Serial收集器是最基本、发展历史最悠久的单线程收集器,最大的特点是Stop The World,垃圾回收时要终止用户进程,等GC结束用户进程方可继续。

JVM参数-XX:+UseSerialGC

3.4.2 ParNew收集器

这里写图片描述
ParNew收集器是Serial收集器的多线程版本。

3.4.3 Parallel Scavenge收集器

Parallel Scavenge收集器是一个新生代收集器,它也是使用复制算法的收集器,又是并行的多线程收集器。

3.4.4 Serial Old收集器

Serial Old是Serial收集器的老年代版本,它同样是一个单线程收集器,使用标记-整理算法。

3.4.5 Parallel Old收集器

Parallel Old是Parallel Scavenge收集器的老年代版本,使用多线程和“标记-整理”算法。

3.4.6 CMS收集器

这里写图片描述

CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。目前很大一部分的Java应用集中在互联网站或者B/S系统的服务端上,这类应用尤其重视服务的响应速度,希望系统停顿时间最短,以给用户带来较好的体验。CMS收集器就非常符合这类应用的需求。

3.4.7 G1收集器

G1 (Garbage-First)是一款面向服务器的垃圾收集器,主要针对配备多颗处理器及大容量内存的机器. 以极高概率满足GC停顿时间要求的同时,还具备高吞吐量性能特征。

3.4 GC范围

按范围分,GC有Minor GC、Major GC和Full GC。
从年轻代空间(包括 Eden 和 Survivor 区域)回收内存被称为 Minor GC,从老年代回收内存被称为Major GC,同时回收新生代和老年代称为Full GC。

四、内存分配策略

4.1 对象优先分配在Eden区

对象优先在新生代Eden中分配,Eden中没有足够空间进行分配时,虚拟机发起一次Minor GC。

4.2 大对象直接进入老年代

大对象是指需要大量连续内存空间的Java对象,典型的是长字符串和数组,这种大对象会被分配到老年代之中。

4.3 老不死的对象进入老年代

新生代中的对象经过不断GC仍然存活,达到一定的“年龄”就会进入老年代,每经历一次Minor GC,年龄+1,达到15时(默认)就会进入老年代。晋升老年代的年龄阈值可以通过-XX:MaxTenuringThreshold参数指定。

4.4 动态对象年龄判定

JVM也不是严格执行4.3中的年龄要求,Survivor空间相同年龄所有对象大小的总和大于等于Suivivor空间的一半,年龄大于或等于该年龄的对象就会直接进入老年代,无需达到XX:MaxTenuringThreshold的要求年龄。

4.5 空间分配担保

Minor GC之前,虚拟机先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果是,GC是确保安全的。JVM继续检查老年代最大的可用连续空间是否大于历次晋升到老年代的对象的平均大小,如果大于,则正常进行一次YGC,尽管有风险(因为判断的是平均大小,有可能这次的晋升对象比平均值大很多);
如果小于,或者HandlePromotionFailure设置不允许空间分配担保,这时要进行一次Full GC。

五、小结

JVM这一部分初步整理这么多,欢迎批评指正。

目录
相关文章
|
1月前
|
Java
jvm复习,深入理解java虚拟机一:运行时数据区域
这篇文章深入探讨了Java虚拟机的运行时数据区域,包括程序计数器、Java虚拟机栈、本地方法栈、Java堆、方法区、元空间和运行时常量池,并讨论了它们的作用、特点以及与垃圾回收的关系。
60 19
jvm复习,深入理解java虚拟机一:运行时数据区域
|
24天前
|
缓存 算法 Java
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
这篇文章详细介绍了Java虚拟机(JVM)中的垃圾回收机制,包括垃圾的定义、垃圾回收算法、堆内存的逻辑分区、对象的内存分配和回收过程,以及不同垃圾回收器的工作原理和参数设置。
48 4
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
|
22天前
|
存储 监控 算法
美团面试:说说 G1垃圾回收 底层原理?说说你 JVM 调优的过程 ?
尼恩提示: G1垃圾回收 原理非常重要, 是面试的重点, 大家一定要好好掌握
美团面试:说说 G1垃圾回收 底层原理?说说你 JVM 调优的过程  ?
|
24天前
|
存储 SQL 小程序
JVM知识体系学习五:Java Runtime Data Area and JVM Instruction (java运行时数据区域和java指令(大约200多条,这里就将一些简单的指令和学习))
这篇文章详细介绍了Java虚拟机(JVM)的运行时数据区域和JVM指令集,包括程序计数器、虚拟机栈、本地方法栈、直接内存、方法区和堆,以及栈帧的组成部分和执行流程。
24 2
JVM知识体系学习五:Java Runtime Data Area and JVM Instruction (java运行时数据区域和java指令(大约200多条,这里就将一些简单的指令和学习))
|
24天前
|
Arthas 监控 Java
JVM知识体系学习七:了解JVM常用命令行参数、GC日志详解、调优三大方面(JVM规划和预调优、优化JVM环境、JVM运行出现的各种问题)、Arthas
这篇文章全面介绍了JVM的命令行参数、GC日志分析以及性能调优的各个方面,包括监控工具使用和实际案例分析。
38 3
|
26天前
|
算法 Java
谈谈HotSpot JVM 中的不同垃圾回收器
【10月更文挑战第5天】理解 HotSpot JVM 中的不同垃圾回收器(如 CMS、G1 和 ZGC)的区别,需要深入了解它们的设计原理、工作方式和应用场景。以下是对这三个垃圾回收器的简要概述以及一个示例 Java 程序,虽然示例程序本身不能直接展示垃圾回收器的内部机制,但可以帮助观察不同垃圾回收器的行为。
15 1
|
2月前
|
存储 算法 Java
深入解析 Java 虚拟机:内存区域、类加载与垃圾回收机制
本文介绍了 JVM 的内存区域划分、类加载过程及垃圾回收机制。内存区域包括程序计数器、堆、栈和元数据区,每个区域存储不同类型的数据。类加载过程涉及加载、验证、准备、解析和初始化五个步骤。垃圾回收机制主要在堆内存进行,通过可达性分析识别垃圾对象,并采用标记-清除、复制和标记-整理等算法进行回收。此外,还介绍了 CMS 和 G1 等垃圾回收器的特点。
104 0
深入解析 Java 虚拟机:内存区域、类加载与垃圾回收机制
|
1月前
|
存储 Java PHP
【JVM】垃圾回收机制(GC)之引用计数和可达性分析
【JVM】垃圾回收机制(GC)之引用计数和可达性分析
50 0
|
3月前
|
C# UED 开发者
WPF动画大揭秘:掌握动画技巧,让你的界面动起来,告别枯燥与乏味!
【8月更文挑战第31天】在WPF应用开发中,动画能显著提升用户体验,使其更加生动有趣。本文将介绍WPF动画的基础知识和实现方法,包括平移、缩放、旋转等常见类型,并通过示例代码展示如何使用`DoubleAnimation`创建平移动画。此外,还将介绍动画触发器的使用,帮助开发者更好地控制动画效果,提升应用的吸引力。
146 0
|
24天前
|
存储 安全 Java
jvm 锁的 膨胀过程?锁内存怎么变化的
【10月更文挑战第3天】在Java虚拟机(JVM)中,`synchronized`关键字用于实现同步,确保多个线程在访问共享资源时的一致性和线程安全。JVM对`synchronized`进行了优化,以适应不同的竞争场景,这种优化主要体现在锁的膨胀过程,即从偏向锁到轻量级锁,再到重量级锁的转变。下面我们将详细介绍这一过程以及锁在内存中的变化。
34 4