> 文档中心 > 一文让你入门JVM

一文让你入门JVM


1.什么是JVM?

​ JVM就是Java的虚拟机,Java文件执行步骤:将.java文件编译成.class文件,再通过JVM(虚拟机)将.class文件转换成对应系统的字节码文件,JVM是Java跨平台的重要实现。

一文让你入门JVM

2.JVM的体系结构

一文让你入门JVM

3.类加载器

1.首先要知道为什么需要类加载器。

  • JVM将指定的.class文件加载到内存,并运行class文件中的Java程序,这个过程被称作为类的加载。

2.类加载的过程。

  • JVM会通过加载、连接、初始化3个步骤来对该类进行初始化。

一文让你入门JVM

3.类加载器

  • 引导类加载器:BootstrapclassLoader:主要负责加载核心的类库(java.lang.*等),构造ExtClassLoader和APPClassLoader。
  • 扩展类加载器:ExtClassLoader:主要负责加载jre/lib/ext目录下的一些扩展的jar。
  • 系统类加载器:AppClassLoader:主要负责加载应用程序的主函数类。

4.双亲委派机制

​ Java虚拟机加载类的方式是按需加载,当需要这个类时再将class文件加载到内存中,将class文件加载成class对象。JVM加载某个class文件时,先询问当前加载器是否加载,若没加载则交给父类的加载器进行加载。

​ 类的加载过程:

一文让你入门JVM

双亲委派机制流程图:

一文让你入门JVM

为什么要设计这种机制

​ 这种设计有个好处是,如果有人想替换系统级别的类:String.java。篡改它的实现,在这种机制下这些系统的类已经被BootstrapclassLoader加载过了(为什么?因为当一个类需要加载的时候,最先去尝试加载的就是BootstrapClassLoader),所以其他类加载器并没有机会再去加载,从一定程度上防止了危险代码的植入。

5.沙箱安全机制

​ Java安全模型的核心就是Java沙箱,什么是沙箱?沙箱是一个限制程序运行的环境。沙箱机制就是将 Java 代码限定在JVM特定的运行范围中,并且严格限制代码对本地系统资源访问,通过这样的措施来保证对代码的有效隔离,防止对本地系统造成破坏。沙箱主要限制系统资源访问,那系统资源包括什么?——CPU、内存、文件系统、网络。不同级别的沙箱对这些资源访问的限制也可以不一样。

Java中的安全机制(早期)

​ 在Java中将执行程序分为了两个部分,一个是本地代码,一个是远程代码。本地方法受信任可以调用许多系统资源,远程代码存在于沙箱中被隔离,无法使用系统资源。而对于非授信的远程代码在早期的Java实现中。

一文让你入门JVM

JDK1.2的安全机制(现在)

​ 在当前的安全机制中,引用了域的概念,虚拟机将分成系统域和应用域。系统域专门负责和系统的关键资源进行管理。应用域则通过调用系统域的部分权限来使用。虚拟机中不同的受保护域,对应不一样的权限存在于不同域中的类文件就具有了当前域的全部权限。

一文让你入门JVM

6.Native关键字

凡是带了native 关键字的,说明java 的作用范围达不到了,会去调用底层C 语言的库。
凡是带了native 关键字的,会进入本地方法栈。

调用JNI:Java Native InterFace (java本地接口)

JNI是为了使用C,C++语言而提供的接口。

使用了Native后会在内存中开辟一块 Native Method Stack,用来存放本地方法。

执行的时候会通过JNI来调用 本地方法库的方法。

一文让你入门JVM

7.PC寄存器

程序计数器:Program Counter Register
 每个线程都有一个程序计数器,是线程私有的,就是一个指针,指向方法区中的方法字节码(用来存储指向一条指令的地址,也即将要执行的指令代码),在执行引擎读取下一条指令,是一个非常小的内存空间,几乎可以忽略不计。

8.方法区

Method Area 方法区
  方法区是被线程共享,所有字段和方法字节码,以及一些特殊方法,如构造函数,接口代码也在此定义,简单说,所有定义的方法的信息都保存在该区域,此区域属于共享空间。

静态变量、常量、类信息(构造方法、接口定义)、运行时的常量池存在方法区中,但是,实例变量存在堆内存中,和方法区无关。

9.深入理解栈

程序=数据结构+算法

栈:先进后出、后进先出(就像枪的弹夹,按压子弹,和射出子弹的过程)

栈内存,主管程序的运行,生命周期与线程同步;
线程结束,栈内存也就释放,对于栈来说,不存在垃圾回收问题,一旦线程结束,栈就over了

队列:先进先出(就像你在食堂排队打饭)

栈运行原理:栈帧

程序正在执行的方法,一定在栈的顶部

一文让你入门JVM

堆,栈,方法区之间的关系。

实现String s=new String(“aa”);

一文让你入门JVM

10.三种JVM

  1. Sun公司 HostSpot Java HotSpot™ 64-Bit Server VM (build 25.101-b13, mixed mode)
  2. BEA Jrockit
  3. IBM J9 VM

11.堆内存

Heap,一个JVM 只有一个堆内存,堆内存的大小是可以调节的。

类加载器读取了类文件后,一般会把什么东西放到堆中?
类的实例、方法、常量、变量~,保存我们所有引用类型的真实对象

堆内存中还要细分为三个区域:

  1. 新生区 (伊甸园区) Young/New
  2. 养老区 old
  3. 永久区 Perm

一文让你入门JVM

1.GC 垃圾回收主要是在伊甸园区和养老区~
  2.假设内存满了,OOM ,堆内存不够!
  3.在JDK 8以后,永久存储区改了个名字(元空间)

OOM

OOM堆内存溢出错误 java.lang.OutOfMemoryError

package JVM;import java.util.Random;public class OOMDemo {    public static void main(String[] args) { String s="Java小羽"; while(true){     s+=s+new Random().nextInt(999999999); }    }}

执行结果:

一文让你入门JVM

12.新生区,老年区

1.所有new的对象都是在伊甸园区创建。

2.当伊甸园区对象为10个的时候,触发轻GC。清理没有被引用的对象。

3.幸存的对象存储在,幸存者0区或幸存者1区,(这两个区域是交替执行的)。

4.当幸存区满了就会触发 重GC,清理幸存区的内存空间。

5.剩下的对象被存储在老年区

一文让你入门JVM

(就跟一场战争一样,不断的活下来)

真理:经过研究,99%的对象都是临时对象! new

13.永久区

1.jdk 1.6之前:永久代,常量池是在方法区中
2.jdk 1.7 :永久代, 但是慢慢的退化了,去 永久代,常量池在堆中
3.jdk 1.8 之后:无永久代,常量池在元空间

永久区常驻内存的,用来存放JDK自身携带的class对象,interface元数据,存储的是Java运行时的一些环境或类信息这个区域不存在垃圾回收!关闭虚拟机就会释放这个区域的内存。

OOM出现条件:一个启动器,加载了大量的第三方jar包、Tomcat部署了太多应用,大量动态生成的反射类,直到内存满。

一文让你入门JVM

元空间逻辑上存在,物理上不存在。

元空间使用的是计算机物理内存,不是JVM的内存

14.堆内存调优

垃圾回收

打开配置

一文让你入门JVM

配置:-Xms1024m -Xmx1024m -XX:+PrintGCDetails

-Xms1024m :JVM初始分配的内存大小为1024m
-Xmx1024m :JVM最大分配的内存大小为1024m
-Xmn512m:设置年轻代的大小,设置越大,gc越少.

一文让你入门JVM

测试代码:

package JVM;public class Memory {    public static void main(String[] args) { //返回虚拟机试图使用的最大内存 long max=Runtime.getRuntime().maxMemory(); //返回JVM的初始化总内存 long total=Runtime.getRuntime().totalMemory(); System.out.println("max="+max+"字节\t"+(max)/(double)1024/1024+"MB"); System.out.println("total="+total+"字节\t"+(total)/(double)1024/1024+"MB"); //默认情况下试图使用的总内存时电脑内存的1/4,而JVM初始化内存是1/64.    }}

结果:

一文让你入门JVM

可以看出JVM在创建对象时内存存储的细节。

15.GC:垃圾回收机制

垃圾回收的区域只会在堆里。

JVM在进行GC时,并不是对三个区域一起回收,大部分都是对新生代进行回收。

  • 新生代
  • 幸存区(form,to)
  • 老年区

GC两种类:轻GC(普通的GC),重GC(全局GC)

  • 轻GC 指针对 新生代 和 偶尔走一下 幸存区。

  • 重GC 全部清完。

相关面试题

1.JVM的内存模型和分区~详细到每一个区放什么?
  2.堆里面的分区有哪些?
  3.GC的算法有哪些?
    标记清除法
    标记整理
    复制算法
    引用计数法
    怎么用的?
  4.轻GC 和 重GC 分别再什么时候发生?

1.引用计数法

简单来说,就是给每个对象分配一个计数器,每调用一次对象就给当前这个对象的计数器加一。

当调用GC的时候将计数器为0的对象进行清除。

一文让你入门JVM

现在JVM不使用这种算法,效率不高。

2.复制算法

每次GC 都会将 伊甸园区 活得对象 移动到 幸存区 中,如果幸存区放不下 ,就移到养老区中。
  一旦伊甸园区被GC 后,就会是空的。
  当某对象从伊甸园区 存活下来了。

谁空谁是to
  假设这个对象还活着,它就把这个对象复制到另一个区域 要么是 form 要么是 to

当某个对象 经历15次(默认值)GC 都还没有死的时候,就会进入养老区。

一文让你入门JVM

详细解释复制算法

幸存区里面,to 是空的,form 里面有对象。

1.伊甸园区的对象满了,进行一次轻GC,存活的对象移动到to区。

一文让你入门JVM

2.from区的对象移动到to区,每次GC清理完后from区和伊甸园区都是空的。

3.经历15次GC之后,会把幸存区 里面存活的对象 移到养老区。

一文让你入门JVM

好处:没有内存的碎片
坏事:浪费一半内存的空间:多了一半空间用于是空的。

赋值算法最佳使用场景:对象存活度较低的时候;新生区

3.标记清除算法

扫描这些对象,对活着的对象进行标记
清除:对没有标记的对象,进行清除

一文让你入门JVM

优点:不需要额外的空间
缺点:两次扫描严重浪费时间,会产生内存碎片。

4.标记压缩算法

优化

一文让你入门JVM

内存效率:复制算法 > 标记清除算法 > 标记压缩算法(时间复杂度)
 内存整齐度:复制算法 = 标记压缩算法 > 标记清除算法
 内存利用率: 标记压缩算法 = 标记清除算法 > 复制算法

没有最优算法吗?
  没有;
  没有最好的算法,只有最合适的算法~ ---->GC: 分代收集算法
  每一代用合适的算法就好了。

年轻代: (大部分的对象都在这里都死了)
  存活率低
  复制算法!

老年代:
  存活率高,区域大
点:不需要额外的空间
缺点:两次扫描严重浪费时间,会产生内存碎片。

4.标记压缩算法

优化

[外链图片转存中…(img-UhUeEhYv-1650761306791)]

内存效率:复制算法 > 标记清除算法 > 标记压缩算法(时间复杂度)
 内存整齐度:复制算法 = 标记压缩算法 > 标记清除算法
 内存利用率: 标记压缩算法 = 标记清除算法 > 复制算法

没有最优算法吗?
  没有;
  没有最好的算法,只有最合适的算法~ ---->GC: 分代收集算法
  每一代用合适的算法就好了。

年轻代: (大部分的对象都在这里都死了)
  存活率低
  复制算法!

老年代:
  存活率高,区域大
  标记清除 + 标记压缩 混合实现