【Java】5分钟带你了解什么是JVM?

简介: JVM是JavaVirtualMachine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。

什么是JVM

JVM是JavaVirtualMachine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。

JVM的体系结构

  • 本地方法接口:JNI (Native MMethod Interface)
  • 执行引擎:Interpreter 解释器 ,JIT Compiler 即使编译器 (执行方法中代码时,代码通过执行引擎执行中的“解释器”执行;方法中经常调用的代码,即热点代码,通过“即时编译器”执行,其执行速度非常快。) , GC 垃圾回收
  • 栈:局部变量表(8大基本数据类型,对象的引用,returnAddress 类型),动态链接(也叫 指向运行时常量池的引用)、方法出口
  • 方法区:类的元信息(类的属性、成员变量、构造函数等),常量池(1.8之后存在元空间,之前在永久代),静态变量(1.7后存入堆)
  • 堆:所有的对象实例和 数组
  • 本地方法区:调用本地方法的一些参数和返回值
  • 程序计数器:存放字节码指令地址

详细图
截图.png

类加载器

  • 作用: 将Java程序编译完的Class文件 加载成 字节码文件 进入JVM
  • 分类:

1、 启动类(根加载器)加载器 (C++ 实现的)
2、 扩展类加载器 ExtClassLoader 负责加载/jre/lib/ext目录下的jar包。
3、 应用程序(系统)加载器 AppClassLoader 负责加载java -classpath或-D java.class.path所指的目录下的类与jar包。(最常用的加载器)
4、 自定义类加载器 必须继承AppClassLoader

类加载机制可以引申出 双亲委派机制
截图1.png

  • 类加载的过程:

1.类加载器收到类加载请求
2.将请求向上委托,直到 启动类加载器
3.如果父类加载器加载失败,向下找子类加载器加载
4.重复3,如果应用程序类加载器也加载失败, class not found

  • 沙箱安全机制

1.字节码校验器:它保证java代码符合java语言规范,核心类由于已经校验过了封装好的,字节码不会校验核心类
2.类加载器:
*类加载器是利用了双亲委派机制,它保证了好的代码不会被坏的代码污染

  • 它定义了被信任类库的边界

*为代码归入域(类似于分配角色,分配权限)

Native

1.带有native关键字的方法,是说明java作用范围无法达到,会去调用底层c/c++的库
Java在内存中开辟一块区域,本地方法栈,然后登记native方法,通过JNI来调用本地方法库。
native是指Java中调用非Java语言编写的代码的能力,这些非Java语言编写的代码被称为本地代码(Native Code),一般使用JNI(Java Native Interface)来实现。
本地方法栈是JVM中用于执行本地方法的数据结构。在Java中调用本地代码时,JVM会使用本地方法栈来执行本地方法。

程序计数器(PC寄存器)

JVM中一个非常特殊的寄存器,它只能存储指令地址,在JVM执行字节码时,PC寄存器扮演了非常重要的角色,它帮助JVM记录程序执行的位置,保证程序可以按照正确的顺序执行。

方法区

方法区(Method Area)是一块存储Java类和常量的内存区域,用于存放类的结构信息、静态变量(1.7之后存放在堆里)、常量等数据。
方法区是线程共享的内存区域,它在虚拟机启动时被创建,并且随着虚拟机的运行而存在。方法区的大小可以通过启动参数进行调整。
方法区的主要作用如下:
1.存储类信息:方法区存储了类的结构信息,包括类的字段、方法、构造方法、接口等。类加载器加载类时,会将类的信息存储到方法区中。
2.存储静态变量:静态变量是类级别的变量,存储在方法区中,不会被实例化对象所共享。
3.存储常量:常量池是方法区的一部分,它用于存储编译时生成的各种字面量和符号引用,如字符串常量(1.8后已经存放在堆中)、类和接口的全限定名、字段和方法的名称和描述符等。
4.存储运行时常量池:当类加载完成后,JVM会将常量池中的符号引用转化为直接引用,存储在方法区中的运行时常量池中。
需要注意的是,方法区是JVM规范中定义的一块内存区域,实现可以有所不同。在一些较老的JVM实现中,方法区被称为永久代(PermGen),而在新版的JVM中,方法区已经被移除,取而代之的是元空间(Metaspace),这是一块更灵活、更高效的内存区域。

先进后出,主管程序的运行、生命周期和线程同步,线程结束,栈也就over。不存在垃圾回收

栈:8大基本数据类型,对象的引用,实例方法

程序正在执行的方法,一定在栈顶
栈的执行过程可以简单地描述为以下步骤: 先调用后执行
1、当程序遇到一个函数调用时,会将函数的参数、局部变量和返回地址等信息压入栈中。
2、栈顶帧被用来存储当前正在执行的函数的信息,包括局部变量、返回地址和其他临时值。
3、当函数执行完毕后,栈顶帧被弹出,控制返回到原来的调用位置,并将返回值传递回去。
4、栈顶帧被弹出后,栈顶帧变成了前一个调用者的帧,继续执行该函数。
5、如果栈的深度超过了系统所允许的最大深度,将会发生栈溢出异常(StackOverflowError)。

三种JVM

1、HotSpot Sun
2、JRockit BEA
3、J9VM IBM

堆 Heap

一个JVM只有一个堆内存,堆内存的大小是可以调节的
截图2.png

永久代也叫方法区,在1.8之后取消了,替代的是元空间(MetaSpace),元空间与永久代上类似,都是方法区的实现,他们最大区别是:元空间并不在JVM中,而是使用本地内存。
为什么移除永久代? 移除永久代原因:为融合HotSpot JVM与JRockit VM(新JVM技术)而做出的改变,因为JRockit没有永久代。
.堆内存的存储特点----先进先出,后进后出
.堆内存的存储特点----先进先出,后进后出

  1.堆内存逻辑上由新生代 ( Young ),老年代 ( Old )和永久代(Perm)组成,其中新生代 ( Young )又被划分为:Eden、From Survivor(From区)和To Survivor(T区)三个区域

截图3.png

注意事项(重要)

(1) 堆内存逻辑上由新生代 ( Young ),老年代 ( Old )和永久代(Perm)或者Metaspace(元空间)组成. (逻辑上:堆=新生+老年+永久或者元空间)
(2)堆内存物理上由新生代 ( Young )和老年代 ( Old )组成,也就是堆内存的大小等于新生代的大小+老年代的大小(物理上:堆=新生+老年)

新生代

新生代是类的诞生、成长、消亡的区域,一个类在这里产生,应用,最后被垃圾回收器收集,结束生命;新生代又分为两部分:伊甸区(Eden)和两个幸存者区(From区,To区)

新生代的垃圾回收:

     (1)所有的类都是在伊甸区被new出来的,当伊甸园的空间用完时,程序又需要创建对象,JVM的垃圾回收器将对伊甸园区进行垃圾回收(MinorGC),将伊甸园区中的不再被其他对象所引用的对象进行销毁。然后将伊甸园中的剩余对象移动到幸存0区。若幸存0区也满了,再对该区进行垃圾回收,然后移动到1 区。那如果1 区也满了,再移动到养老区。若养老区也满了,那么这个时候将产生MajorGC(FullGC),进行养老区的内存清理。
     (2)Minor GC和Full GC 有什么不同呢?
     Minor GC/Young GC:指发生新生代的的垃圾收集动作,Minor GC非常频繁,回收速度一般也比较快。
     Major GC/Full GC:一般会回收老年代 ,年轻代,方法区的垃圾,Major GC的速度一般会比Minor GC的慢10倍以上。

新生的垃圾回收过程(复制->清空->互换)
因为年轻代中的对象基本都是朝生夕死的(80%以上),所以在年轻代的垃圾回收算法使用的是复制算法
stickPicture.png

  1.eden,From区复制到To区,年龄+1

首先,当Eden区满的时候会触发第一次GC,把还活着的对象拷贝到From区,当Eden区再次触发GC的时候会扫描Eden区和From区域,对这两个区域进行垃圾回收,经过这次回收后还存活的对象,则直接复制到To区域(如果有对象的年龄已经达到了老年的标准,则赋值到老年代区),同时把这些对象的年龄+1

  2.清空,eden区,From区

然后,清空Eden和From区中的对象,也即复制之后有交换,谁空谁是to

  3.To区和From区互换

最后,To区和From区互换,原To区成为下一次GC时的From区。部分对象会在From区和To区域中复制来复制去,如此交换15次(次数可以调节)最终如果还是存活,就存入到老年代

老年代

主要接收由年轻代发送过来的对象,一般情况下,经过了数次Minor GC 之后还会保存下来的对象才会进入到老年代,当老年代内存不足时,将引发 "major GC”,即"Full GC“;

注意:

  1.若养老区执行了FullGC之后发现依然无法进行对象的保存,就会产生OOM异常“OutOfMemoryError”。 
  2.如果出现java.lang.OutOfMemoryError: Javaheap space异常,说明Java虚拟机的堆内存不够,原因有二
    (1)Java虚拟机的堆内存设置不够,可以通过参数-Xms、-Xmx来调整。
    (2)代码中创建了大量大对象,并且长时间不能被垃圾收集器收集(存在被引用),也就所谓内存泄漏

当使用元空间替代了永久代之后,出现 OOM 的几率就被大大降低了

  • 老年代的垃圾回收 Full GC/Major GC:

Full GC是发生在老年代的垃圾收集动作,老年代一般是由标记清除算法或者是标记清除算法与标记整理算法的混合实现,因为老年代里面的对象几乎个个都是在 Survivor 区域中存活过来的,所以,Full GC 发生的次数不会有 Young GC那么频繁,并且做一次 Full GC 要比进行一次 Young GC 的时间更长
尽可能让对象都在新生代里分配和回收,尽量别让太多对象频繁进入老年代,避免频繁对老年代进行垃圾回收,同时给系统充足的内存大小,避免新生代频繁的进行垃圾回收
什么样的对象可以直接进入老年代?

  • 大对象直接进入老年代 (根据对象大小)

     1.大对象就是需要大量连续内存空间的对象,如果对象超过设置大小会直接进入老年代,不会进入年轻代。最典型的大对象就是那种很长的字符串以及数组
     2.好处:为了避免为大对象分配内存时的复制操作而降低效率
     3.JVM参数 -XX:PretenureSizeThreshold 可以设置大对象的大小,然后再执行下上面的第一个程序会发现大对象直接进了老年代 ,不过这个参数只在 Serial 和ParNew两个收集器下

有效。

  • 长期存活的对象将进入老年代(根据存活时间)

    1.虚拟机给每个对象定义了一个对象年龄(Age)计数器,如果对象在Eden出生并经过第一次Minor GC后仍然存活,并且能被Survivor容纳的话,将被移动到Survivor空间中,并且对象年龄设为1,。对象在Survivor区中每熬过一次Minor GC,年龄就增加1,以此类推。当他的年龄增加到一定程度, 就将会被晋升到老年代中.
    2.对象晋升到老年代的年龄阈值,可以通过参数 -XX:MaxTenuringThreshold 来设置,默认为15岁,CMS收集器默认6岁,不同的垃圾收集器会略微有点不同
  • 对象动态年龄判断

    1.为了能更好地适应不同程度的内存状况,虚拟机并不是永远地要求对象的年龄必须达到了MaxTenuringThreshold才能晋升到老年代,如果在Survivor区空间中相同年龄的所有对象大小的总和大于Survivor空间的一半,那么此时大于等于这批对象年龄最大值的对象,就可以直接进入老年代了        
    2.例如Survivor区域里现在有一批对象,年龄1+年龄2+年龄n的多个年龄对象总和超过了Survivor区域的50%,此时就会

把年龄n(含)以上的对象都放入老年代。这个规则其实是希望那些可能是长期存活的对象,尽早进入老年代。对象动态年龄判断机制一般是在minor gc之后触发的

   3.这个百分比可以通过参数-XX:TargetSurvivorRatio设置

永久代 (1.8之前)

永久代和元空间可以说是方法区的具体实现。
被装载进此区域的数据是不会被垃圾回收器回收掉的,关闭JVM 才会释放此区域所占用的内存

元空间(1.8)

元空间的大小仅受本地内存限制
元空间与永久代之间最大的区别在于:永久代带使用的JVM的堆内存,但是java8以后的元空间并不在虚拟机中而是使用本机物理内存。
当元空间溢出时会得到如下错误: java.lang.OutOfMemoryError: MetaSpace
元空间、永久代与方法区的关系

  1. 方法区是Java虚拟机规范中的定义,是一种规范,而永久代和元空间是一种实现,一个是标准的一个是实现。
  2. 永久代物理是堆的一部分,和新生代,老年代地址是连续的,而元空间属于本地内存;存储内容不同,元空间存储类的元信息,静态变量和常量池等并入堆中。相当于永久代的数据被分到了堆和元空间中

hotspot中的垃圾回收器

HotSpot 是 Java 虚拟机的一种实现,其默认的垃圾回收器取决于 Java 版本和运行环境。在 Java 8 中,HotSpot 的默认垃圾回收器是 Parallel Scavenge(年轻代)和 Parallel Old(老年代),但在一些特定情况下也会使用 CMS(Concurrent Mark Sweep)垃圾回收器。在 Java 9 中,HotSpot 默认使用 G1 垃圾回收器,这是一种全新的垃圾回收器,旨在提高吞吐量和减少停顿时间。需要注意的是,HotSpot 的垃圾回收器可以通过命令行参数进行配置和更改。

相关文章
|
1月前
|
监控 算法 Java
Java虚拟机(JVM)的垃圾回收机制深度解析####
本文深入探讨了Java虚拟机(JVM)的垃圾回收机制,旨在揭示其背后的工作原理与优化策略。我们将从垃圾回收的基本概念入手,逐步剖析标记-清除、复制算法、标记-整理等主流垃圾回收算法的原理与实现细节。通过对比不同算法的优缺点及适用场景,为开发者提供优化Java应用性能与内存管理的实践指南。 ####
|
25天前
|
监控 算法 Java
Java虚拟机(JVM)垃圾回收机制深度剖析与优化策略####
本文作为一篇技术性文章,深入探讨了Java虚拟机(JVM)中垃圾回收的工作原理,详细分析了标记-清除、复制算法、标记-压缩及分代收集等主流垃圾回收算法的特点和适用场景。通过实际案例,展示了不同GC(Garbage Collector)算法在应用中的表现差异,并针对大型应用提出了一系列优化策略,包括选择合适的GC算法、调整堆内存大小、并行与并发GC调优等,旨在帮助开发者更好地理解和优化Java应用的性能。 ####
32 0
|
22天前
|
存储 监控 算法
深入探索Java虚拟机(JVM)的内存管理机制
本文旨在为读者提供对Java虚拟机(JVM)内存管理机制的深入理解。通过详细解析JVM的内存结构、垃圾回收算法以及性能优化策略,本文不仅揭示了Java程序高效运行背后的原理,还为开发者提供了优化应用程序性能的实用技巧。不同于常规摘要仅概述文章大意,本文摘要将简要介绍JVM内存管理的关键点,为读者提供一个清晰的学习路线图。
|
24天前
|
存储 监控 算法
Java虚拟机(JVM)垃圾回收机制深度解析与优化策略####
本文旨在深入探讨Java虚拟机(JVM)的垃圾回收机制,揭示其工作原理、常见算法及参数调优方法。通过剖析垃圾回收的生命周期、内存区域划分以及GC日志分析,为开发者提供一套实用的JVM垃圾回收优化指南,助力提升Java应用的性能与稳定性。 ####
|
28天前
|
机器学习/深度学习 监控 算法
Java虚拟机(JVM)的垃圾回收机制深度剖析####
本文深入探讨Java虚拟机(JVM)的垃圾回收机制,揭示其工作原理、常见算法、性能调优策略及未来趋势。通过实例解析,为开发者提供优化Java应用性能的思路与方法。 ####
44 1
|
28天前
|
Oracle 安全 Java
深入理解Java生态:JDK与JVM的区分与协作
Java作为一种广泛使用的编程语言,其生态中有两个核心组件:JDK(Java Development Kit)和JVM(Java Virtual Machine)。本文将深入探讨这两个组件的区别、联系以及它们在Java开发和运行中的作用。
73 1
|
1月前
|
监控 Java 开发者
Java虚拟机(JVM)深度优化指南####
本文深入探讨了Java虚拟机(JVM)的工作原理及其性能优化策略,旨在帮助开发者通过理解JVM的内部机制来提升Java应用的运行效率。不同于传统的技术教程,本文采用案例分析与实战技巧相结合的方式,为读者揭示JVM调优的艺术。 ####
58 8
|
2月前
|
Java
jvm复习,深入理解java虚拟机一:运行时数据区域
这篇文章深入探讨了Java虚拟机的运行时数据区域,包括程序计数器、Java虚拟机栈、本地方法栈、Java堆、方法区、元空间和运行时常量池,并讨论了它们的作用、特点以及与垃圾回收的关系。
72 19
jvm复习,深入理解java虚拟机一:运行时数据区域
|
2月前
|
存储 SQL 小程序
JVM知识体系学习五:Java Runtime Data Area and JVM Instruction (java运行时数据区域和java指令(大约200多条,这里就将一些简单的指令和学习))
这篇文章详细介绍了Java虚拟机(JVM)的运行时数据区域和JVM指令集,包括程序计数器、虚拟机栈、本地方法栈、直接内存、方法区和堆,以及栈帧的组成部分和执行流程。
45 2
JVM知识体系学习五:Java Runtime Data Area and JVM Instruction (java运行时数据区域和java指令(大约200多条,这里就将一些简单的指令和学习))
|
1月前
|
监控 算法 Java
深入理解Java虚拟机(JVM)的垃圾回收机制
【10月更文挑战第21天】 本文将带你深入了解Java虚拟机(JVM)的垃圾回收机制,包括它的工作原理、常见的垃圾收集算法以及如何优化JVM垃圾回收性能。通过本文,你将对JVM垃圾回收有一个全新的认识,并学会如何在实际开发中进行有效的调优。
49 0