### 1、什么是JVM
Java[虚拟机](https://baike.baidu.com/item/虚拟机?fromModule=lemma_inlink)(JVM)一种用于计算机设备的规范,
Java虚拟机(JVM)是可运行Java代码的假想计算机。只要根据JVM规格描述将[解释器](https://baike.baidu.com/item/解释器?fromModule=lemma_inlink)移植到特定的计算机上,就能保证经过编译的任何Java代码能够在该系统上运行。
Java虚拟机是一个想象中的机器,在实际的计算机上通过软件模拟来实现。Java虚拟机有自己想象中的硬件,如处理器、堆栈、寄存器等,还具有相应的指令系统。
Java虚拟机规范定义了一个抽象的——而非实际的——机器或处理器。这个规范描述了一个指令集,一组寄存器,一个堆栈,一个“[垃圾堆](https://baike.baidu.com/item/垃圾堆/346930?fromModule=lemma_inlink)”,和一个方法区。一旦一个Java虚拟机在给定的平台上运行,任何Java程序(编译之后的程序,称作字节码)都能在这个平台上运行。 俗称一次编译,到处运行,运行图例如下
### 2、JVM的体系结构
1、方法区:
所有线程共享
静态变量,常量,类信息(构造方法,接口定义)运行时的常量池也存在方法区中,但是实例变量存在堆内存中,和方法区无关
方法区的大小决定了系统可以保存多少个类,如果系统定义太多的类,导致方法区溢出。虚拟机同样会抛
出内存溢出错误。方法区可以理解为永久区(Perm)。
注意:
方法区是被所有线程共享,所有字段和方法字节码,以及一些特殊方法,如构造函数,接口代码也在此定义,简单说,所有定义的方法的信息都保存在该区域,此区域属于共享区间;
静态变量、常量、类信息(构造方法、接口定义、普通方法)、运行时的常量池存在方法区中,但是实例变量(普通变量)存在堆内存中,和方法区无关
方法区:绝对不是放方法的地方,他是存储的每一个类的结构信息(比如static)
永久代和元空间的解释:
方法区是一种规范,类似于接口定义的规范:List list = new ArrayList();
把这种比喻用到方法区则有:
java 7中:`方法区 f = new 永久代();`
java 8中:`方法去 f = new 元空间();`
2、栈
栈:先进后出,后进先出,堆:先进先出,后进后出
线程结束,栈内存释放
主管程序的运行
存放基本类型,实例方法,对象引用…
栈帧:父帧子帧,每一个在执行的方法都会产生栈帧
3、java堆
java堆是和java应用程序关系最密切的内存空间,几乎所有的对象都存放在其中,并
且java堆活全是自动化管理的,通过垃圾回收机制,垃圾对象会自动清理,不需要显
示地释放。
根据垃圾回收机制不同,Java堆有可能拥有不同的结构。最为常见的就是将整个java
堆分为新生代和老年代。其中新生代存放新生的对象或者年龄不大的对象(刚实例化出来的对象),老年代则存放老年对象。
新生代分为eden区(对象刚实例化)、s0区、s1区,s0和s1也被称为from和to区域(JAVA GC 回收的复制算法),他们是两块大小相等并且可以互换角色的空间。
绝大多数情况下,对象首先分配在eden区,在一次新生代回收后,如果对象还存活,
则会进入s0或者s1区,之后每经过一次新生代回收,如果对象存活则它的年龄就加1,
当对象达到一定的年龄后,则进入老年代
4、栈和堆的区别:
栈是运行时的单位,堆是存储的单位。
即,栈解决程序的运行问题,即程序如何执行,或者说如何处理数据。堆解决的是数据存储的问题,即数据怎么放,放在哪里。
栈是线程私有,不存在垃圾回收
虚拟机栈的生命周期同线程一致
栈帧的概念:java中的方法被扔进虚拟机的栈空间之后就成为“栈帧”,比如main方法,是程序的入口,被压栈之后就成为栈帧。
栈的作用:
主管Java程序的运行,它保存方法的局部变量(8种基本数据类型、对象的引用地址)、部分结果,并参与方法的调用和返回。
5、堆、栈、方法区概念和联系
堆解决的是数据存储的问题,即数据怎么放、放在哪儿.
栈解决程序的运行问题,即程序如何执行,或者说如何处理数据。
方法区则是辅助堆栈的快永久区(Perm),解决堆栈信息的产生,是先决条件。
我们创建一个新的对象,User:那么User类的一些信息(类信息、静态信息都存在于方法区中)
而User类被实例化出来之后,被存储到java堆中,一块内存空间
当我们去使用的时候,都是使用User对象的引用,形如User user = new User();
这里的user就是存放在java栈中的,即User真实对象的一个引用。
### 3、类加载器
1、启动类加载器(BootStrap ClassLoader):主要负责加载jre/lib/rt.jar相关的字节码文件的。
扩展类加载器(Extension Class Loader):主要负载加载 jre/lib/ext/*.jar 这些jar包的。
应用程序类加载器(Application Class Loader):主要负责加载用户自定义的类以及classpath环境变量所配置的jar包的。
自定义类加载器(User Class Loader):负责加载程序员指定的特殊目录下的字节码文件的。大多数情况下,自定义类加载器只需要继承ClassLoader这个抽象类,重写findClass()和loadClass()两个方法即可。
2.类加载时机:
1> 创建类的实例(对象)
2> 调用类的实例方法
3>访问类或者接口的类变量,或者为该类变量赋值
4> 使用反射方法来强制创建某个类或接口对应的java.lang.Class对象
5>初始化某个类的子类
6> 直接使用java.exe命令来运行某个主类
3.类加载的过程
加载:
1>通过一个类的全限定名获取定义此类的二进制流,即通过包名+类名,获取这个类,准备用流进行传输
2> 将这个字节流所代表的静态存储结构转化为运行时数据结构,即将这个类加载到内存中
3> 在内存中生成一个代表这个类的java.lang.Class对象,任何类被使用时,系统都会为创建一个java.lang.Class对象,即加载完毕创建一个class对象
验证:
链接阶段的第一步,这一阶段为了确保Class文件字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机的自身安全,即文件中的信息是否符合虚拟机规范,有没有安全隐患
准备:
负责为类的类变量(被static修饰的变量)分配内存,并且设置默认初始化值,即初始化静态变量
解析:
将类的二进制数据流中的符号引用替换为直接引用,即本类中如果用到了其他的类,此时就需要找到对应的类
初始化:
根据程序员通过程序指定的主观计划去初始化类变量和其他资源,即静态变量赋值以及初始化其他资源
statac String school=“中山大学”
类加载过程小结:
当一个类被使用的时候,才会被加载到内存
类加载的过程:加载、验证、准备、解析、初始化
### 4、垃圾回收
程序计数器、虚拟机栈、本地方法栈是线程私有的,所以会随着线程结束而消亡。 Java 堆和方法区是线程共享的,在程序处于运行期才知道哪些对象会创建,这部分内存的分配和回收都是动态的,垃圾回收所关注的就是这部分内存。
1、垃圾回收算法
1>标记清除算法:先标记出所有需要回收的对象,标记完成后统一回收所有被标记的对象。
最基础的垃圾回收算法,**分为标记和清除两个阶段:**
1.标记出所有需要回收的对象;
2.回收被标记的对象所占用的内存空间;
算法效率较低, 会产生较多内存碎片, 可能会导致大对象找不到可利用空间问题。
2>复制算法:将可用内存分为大小相等的两块,每次只使用其中一块,当这一块内存用完了,就将存活的对象复制到另一块,最后将此块内存一次性清理掉。
按照内存容量将**内存划分为相等大小的两块内存区域**。每次使用时只使用其中的一块,当这一块内存满后将存活的对象复制到另一块内存区域上去,将剩余的已使用的内存回收。
这种算法实现简单,内存效率高,不易产生内存碎片,但是内存使用率被压缩到了以前的一半。当存货对象增多时,复制算法的效率会大大降低。
3>标记整理算法:先标记所有需要回收的对象,然后让所有存活的对象向一端移动,最后直接清理掉边界以外的另一端内存。
4>分代收集算法:把Java堆分为新生代和老年代。新生代中只有少量对象会存活,就选用复制算法;老年代中对象存活率较高,选用标记清除算法。
### 5、JVM内存分代机制
方法区即被称为永久代,而堆中存放的是对象实例,为了回收的时候对不同的对象采用不同的方法,又将堆分为新生代和老年代,默认情况下新生代占堆的1/3,老年代占堆的2/3。
新生代(Young):HotSpot将新生代划分为三块,一块较大的Eden空间和两块较小的Survivor空间,默认比例为8:1:1。
老年代(Old):在新生代中经历了多次GC后仍然存活下来的对象会进入老年代中。老年代中的对象生命周期较长,存活率比较高,在老年代中进行GC的频率相对而言较低,而且回收的速度也比较慢。
永久代(Permanent):永久代存储类信息、常量、静态变量、即时编译器编译后的代码等数据,对这一区域而言,一般而言不会进行垃圾回收。
元空间(metaspace):从JDK 8开始,Java开始使用元空间取代永久代,元空间并不在虚拟机中,而是直接使用本地内存。那么,默认情况下,元空间的大小仅受本地内存限制。当然,也可以对元空间的大小手动的配置。
GC 过程:新生成的对象在Eden区分配(大对象除外,大对象直接进入老年代),当Eden区没有足够的空间进行分配时,虚拟机将发起一次Minor GC。GC开始时,对象只会存在于Eden区和Survivor From区,Survivor To区是空的(作为保留区域)。GC进行时,Eden区中所有存活的对象都会被复制到Survivor To区,而在Survivor From区中,仍存活的对象会根据它们的年龄值决定去向,年龄值达到年龄阀值(默认为15,新生代中的对象每熬过一轮垃圾回收,年龄值就加1,GC分代年龄存储在对象的Header中)的对象会被移到老年代中,没有达到阀值的对象会被复制到Survivor To区。接着清空Eden区和Survivor From区,新生代中存活的对象都在Survivor To区。接着,Survivor From区和Survivor To区会交换它们的角色,也就是新的Survivor To区就是上次GC清空的Survivor From区,新的Survivor From区就是上次GC的Survivor To区,总之,不管怎样都会保证Survivor To区在一轮GC后是空的。GC时当Survivor To区没有足够的空间存放上一次新生代收集下来的存活对象时,需要依赖老年代进行分配担保,将这些对象存放在老年代中。
### 6、Jdk和Jre和JVM的区别
Jdk中包括了Jre,Jre中包括了JVM
Jvm在倒数第二层 由他可以在(最后一层的)各种平台上运行
Jre大部分都是 C 和 C++ 语言编写的,他是我们在编译java时所需要的基础的类库
Jdk还包括了一些Jre之外的东西 ,就是这些东西帮我们编译Java代码的, 还有就是监控Jvm的一些工具
JDK(Java Development Kit)即 Java开发工具包,Java的核心
JDK是提供给Java开发人员使用的,JDK是Java程序开发者用来来编译、调试Java程序用的开发工具包,也包括了JRE。所以安装了JDK,就不用在单独安装JRE了,除了包含JRE以外还包含了开发Java程序所必须的命令工具。其中的开发工具:编译工具(javac.exe) 打包工具(jar.exe)等
JDK的工具也是Java程序,也需要JRE才能运行。为了保持JDK的独立性和完整性,在JDK的安装过程中,JRE也是 安装的一部分。所以,在JDK的安装目录下有一个名为jre的目录,用于存放JRE文件
JDK中包含JRE,在JDK的安装目录下有一个名为jre的目录,里面有两个文件夹bin和lib,在这里可以认为bin里的就是JVM,lib中则是jvm工作所需要的类库,而jvm和 lib和起来就称为JRE