1. JVM是什么?
JVM 是 Java Virtual Machine
(Java虚拟机)的缩写。
Java 语言编译程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。这就是J ava的一个特性:与平台的无关性。
有三种Java虚拟机,分别是:HotSpot
、JRockit
、J9VM
。
2. JVM是干什么的?
Java 程序员把内存控制权利交给 Java 虚拟机,在虚拟机自动内存管理机制下,不再需要像 C/C++程序开发程序员这样为每一个 new
操作去写对应的 delete/free
操作,不容易出现内存泄漏和内存溢出问题。
JVM 是管理Java 内存区域的,内存区域是指 JVM 运行时将数据分区域存储,强调对内存空间的划分。
那么了解Java内存区域是如何划分的,对于JVM来说则是必不可少的一步。在了解Java内存区域之后,再了解垃圾回收机制是怎么处理垃圾的。
3. Java内存区域
内存区域是指 JVM 运行时将数据分区域存储,强调对内存空间的划分。
Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干不同的数据区域,这些区域都有各自的用途以及创建和销毁的时间。
JDK1.7及之前,有方法区。JDK1.8之后,取消了方法区,改为元空间 Metaspace
。
Java内存区域包括:虚拟机栈、本地方法栈、程序计数器是线程私有的,在每个线程中是独立存在的。线程共享堆、元空间和直接内存。
![](https://img-blog.csdnimg.cn/20210410110812469.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM1NDM5NTM5,size_16,color_FFFFFF,t_70)
1. 程序计数器
1)程序计数器是什么?
程序计数器是一块较小的内存空间,可以看作是当前线程所执行的字节码的行号指示器。
2)程序计数器是干什么的?
字节码解释器工作时通过改变程序计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等功能都需要依赖程序计数器来完成。
为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各线程之间计数器互不影响,独立存储,这类内存区域是“线程私有”的内存。
3)程序计数器有什么用?
两个作用:
- 字节码解释器通过改变程序计数器来依次读取指令,从而实现代码的流程控制,如:顺序执行、选择、循环、异常处理。
- 在多线程的情况下,程序计数器用于记录当前线程执行的位置,从而当线程被切换回来的时候能够知道该线程上次运行到哪儿了。
4)程序计数器的生命周期?
它的生命周期随着线程的创建而创建,随着线程的结束而死亡。此外,程序计数器是唯一一个不会出现 OutOfMemoryError
的内存区域。
2. 虚拟机栈
1)虚拟机栈是什么?
Java 内存可以粗糙的区分为堆内存(Heap
)和栈内存 (Stack
),其中栈就是现在说的虚拟机栈,或者说是虚拟机栈中局部变量表部分。
Java 虚拟机栈是由一个个栈帧组成,而每个栈帧中都拥有:局部变量表、操作数栈、动态链接、方法出口信息。每一次函数调用都会有一个对应的栈帧被压入栈中,每一个函数调用结束后,都会有一个栈帧被弹出。Java 方法有两种返回方式(return
语句;抛出异常),都会导致栈帧被弹出。
局部变量表主要存放了编译期可知的各种数据类型(boolean
、byte
、char
、short
、int
、float
、long
、double
)、对象引用(reference
类型,它不同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄或其他与此对象相关的位置)。
2)虚拟机栈是干什么的?
虚拟机栈描述的是 Java 方法执行的内存模型,每次方法调用的数据都是通过栈传递的。
3)虚拟机栈有什么用?
虚拟机栈为虚拟机执行 Java 方法 (也就是字节码)服务。为了保证线程中的局部变量不被别的线程访问到,虚拟机栈是线程私有的。
4)虚拟机栈的生命周期?
虚拟机栈是线程私有的,它的生命周期和线程相同。
5)虚拟机栈会出现什么错误?
StackOverFlowError
: 如果 Java 虚拟机栈的内存大小不允许动态扩展,当线程请求栈的深度超过当前 Java 虚拟机栈的最大深度的时候,就抛出 StackOverFlowError
错误。
OutOfMemoryError
:如果 Java 虚拟机栈的内存大小可以动态扩展,虚拟机在动态扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError
异常。
3. 本地方法栈
1)本地方法栈是什么?
本地方法:带Native
关键字的方法,会调用底层C语言的库。本地方法会调用本地方法接口JNI
本地方法被执行的时候,在本地方法栈也会创建一个栈帧,用于存放该本地方法的局部变量表、操作数栈、动态链接、出口信息。
2)本地方法栈有什么用?
本地方法栈是为虚拟机使用到的 Native
方法服务。
3)本地方法栈的生命周期?
本地方法执行完毕后相应的栈帧也会出栈并释放内存空间。
4)本地方法栈会出现什么错误?
两种错误: StackOverFlowError
和 OutOfMemoryError
。
4. 堆
1)堆是什么?
堆是所有线程共享的一块内存区域,也是JVM所管理的内存中最大的一块。
2)堆是干什么的?
存放对象实例,几乎所有的对象实例以及数组都在堆里分配内存。
3)堆的生命周期?
在虚拟机启动时创建。
4)堆内存有哪些组成?
Java 堆是垃圾收集器管理的主要区域。从垃圾回收的角度,由于现在收集器基本都采用分代垃圾收集算法,所以, Java 堆还可以细分为:新生代和老年代:新生代有:Eden
空间、From Survivor
、To Survivor
空间。进一步划分的目的是更好地回收内存,或者更快地分配内存。
在 JDK 7 版本及JDK 7 版本之前,堆内存被通常被分为下面三部分:
-
新生代( Young Generation )
-
老年代( Old Generation )
-
永久代( Permanent Generation )
![](https://img-blog.csdnimg.cn/20210410110940984.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM1NDM5NTM5,size_16,color_FFFFFF,t_70)
JDK 8 版本之后方法区(HotSpot
的永久代)被彻底移除了(JDK1.7 就已经开始了),取而代之是元空间,元空间使用的是直接内存。
![](https://img-blog.csdnimg.cn/20210410111033114.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM1NDM5NTM5,size_16,color_FFFFFF,t_70)
5)永久代中存放什么样子的数据?
-
永久代是常驻内存的,用来存储Java运行时的一些环境或类信息,比如JDK自身的类对象和一些Interface 元数据。
-
没有垃圾回收
-
关闭虚拟机会释放内存
-
什么情况下会崩溃,以下情况不断被加载,直到内存满了之后会出现OOM:
- 一个启动类加载了大量的第三方jar包
- Tomcat 部署了太多的应用
- 大量动态生成的反射类
6)堆会出现什么错误 ?
堆这里最容易出现的就是 OutOfMemoryError
错误,并且出现这种错误之后的表现形式还会有几种,比如:
OutOfMemoryError: GC Overhead Limit Exceeded
: 当JVM花太多时间执行垃圾回收并且只能回收很少的堆空间时,就会发生此错误。java.lang.OutOfMemoryError: Java heap space
:假如在创建新的对象时, 堆内存中的空间不足以存放新创建的对象, 就会引发java.lang.OutOfMemoryError: Java heap space
错误。(和本机物理内存无关,和你配置的内存大小有关!)
5. 方法区
1)方法区是什么?
方法区与 Java 堆一样,是各个线程共享的内存区域。
2)方法区是干什么的?
它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
3)方法区与永久代的区别是什么?
永久代是 HotSpot
(一种Java虚拟机) 的概念,方法区是 Java 虚拟机规范中的定义,是一种规范,而永久代是一种实现,一个是标准一个是实现。
6.元空间
1)元空间是什么?
从Java1.8开始,元空间取代方法区,元空间使用的是直接内存。
2)设置元空间的大小?
// 设置参数
-XX:MetaspaceSize=N //设置 Metaspace 的初始大小,默认值为 unlimited
-XX:MaxMetaspaceSize=N //设置 最大Metaspace 的大小
与永久代很大的不同就是,如果不指定大小的话,随着更多类的创建,虚拟机会耗尽所有可用的系统内存。
3)元空间会出现什么错误?
java.lang.OutOfMemoryError: MetaSpace
:元空间溢出。
7. 直接内存
1)直接内存是什么?
直接内存并不是虚拟机运行时数据区的一部分,也不是虚拟机规范中定义的内存区域,但是这部分内存也被频繁地使用。
直接内存的分配不会受到 Java 堆的限制,但是,会受到本机总内存大小以及处理器寻址空间的限制。
2)直接内存会出现什么错误?
可能会出现 OutOfMemoryError
错误。
8. 哪里有垃圾
栈和程序计数器中没有垃圾。
垃圾回收主要是在堆里。
参考链接:
https://snailclimb.gitee.io/javaguide/#/docs/java/jvm/Java%E5%86%85%E5%AD%98%E5%8C%BA%E5%9F%9F
https://www.bilibili.com/video/BV1iJ411d7jS?from=search&seid=12696469813953935602