JVM

72 阅读7分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

@TOC

一、JVM的位置

在这里插入图片描述

二、JVM的体系结构

在这里插入图片描述

在这里插入图片描述

三、类加载器及双亲委派机制

在这里插入图片描述

1. 类加载器的作用

负责将.class文件给加载到JVM中去执行。

2. 类加载器分类

(1)Bootstrap classLoader:主要负责加载核心的类库(java.lang.*等),构造ExtClassLoader和APPClassLoader。 (2)ExtClassLoader:主要负责加载jre/lib/ext目录下的一些扩展的jar。 (3)AppClassLoader:主要负责加载应用程序的主函数类

3. 双亲委派机制

当一个.class文件需要被加载时,首先会在AppClassLoader中检查是否加载过,如果被加载过那就无需再加载,如果没有被加载过,会接着拿到父加载器ExtClassLoader,然后检查是否加载过,如果被加载过那就无需再加载,如果没有被加载过,会接着拿到父加载器Bootstrap classLoader进行检查是否被加载过。到此,开始从Bootstrap classLoader进行判断是否能加载,如果能加载,则加载,不能则让子类加载器ExtClassLoader、AppClassLoader依次判断去加载,如果一直到最后都无法加载,则会抛出异常ClassNotFoundException

4. 双亲委派机制的优点

防止恶意替换系统级别的类,如下图所示,想替换java.lang.String失败 在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

四、沙箱安全机制

(1)限制应用程序对系统资源的访问的运行环境 (2)沙箱提供的环境相对于每一个运行的程序都是独立的,而且不会对现有的系统产生影响。

五、Native 关键字

1. Native说明

凡是带了native关键字的,说明java的作用范围达不到了,回去调用底层C语言的库。 例如线程中的start0 在这里插入图片描述

2. Native调用方式

首先进去本地方法栈中,通过java本地接口(JNIjava native interface),调用本地方法库中的方法。其中本地方法栈专门用来登记native方法。

3. JNI的作用

扩展java的使用,融合不同的编程语言为java所用。此外还可以连接硬件,如驱动打印机,管理系统等。

六、PC寄存器

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

七、方法区

  1. 方法区是被所有线程共享。
  2. 所有字段和方法字节码,以及一些特殊方法,如构造函数,接口代码也在此定义,简单说,所有定义的方法的信息都保存在该区域,此区域属于共享区间;
  3. 静态变量static、常量final、类信息Class(构造方法、接口定义)、运行时的常量池,他们都存在方法区中,但是实例变量存在堆内存中,和方法区无关。

八、java类在内存实例化的过程

  1. 类实例化首先加载的是静态属性和变量、静态代码块、代码块、最后是构造函数 在这里插入图片描述

  2. 过程 (1)加载类的信息,类中的成员变量、方法体加载到方法区; (2)程序进入main方法,main函数压栈(进入栈区)同时定义了一个变量person,该变量将来指向一个Person实例对象; (3)在堆内存中开辟—块空间,存放new来的对象,并将其在方法区对应的成员变量、成员方法(地址值)拷贝过来,然后让栈中的变量person指向该对象示例 (4)对new出来的person对象赋值,先在栈区找到person变量,然后根据地址值在堆中找到实例对象进行赋值操作。 (5)当程序走到lego方法时,先到栈区找到person这个变量,然后根据该地址值在堆内存中找到实例对象,再进行方法调用。将lego方法压入栈,被调用完成后,就会立刻马上从栈内弹出,最后,在main函数完成后,main函数也会出栈。

九、堆

  1. Heap,一个JVM只有一个堆内存,堆内存的大小是可以调节的。
  2. 类加载器读取了类文件后,一般会把类,方法,常量,变量放入堆中,此外,堆还保存我们所有引用类型的实例对象;
  3. 堆内存中还要细分为三个区域: (1)新生区:包括伊甸园区,幸存0区,幸存1区 (2)养老区 (3)永久区:又名元空间
  4. 垃圾回收一般在伊甸园区,养老区
  5. 假设内存满了,出现OOM,堆内存不够! java.lang.OutOfMemoryError: Java heap space OOM解决方法:

(1)尝试扩大堆内存看结果

-Xms1024m -Xmx1024m -XX:+PrintGCDetails

(2)分析内存,看一下那个地方出现了问题(专业工具>

十、新生区、老年区、永久区

1. 新生区

(1)包括:伊甸园区、幸存1区、幸存2区 (2)新生区是类诞生和成长的地方,甚至死亡; (3)所有的对象都是在伊甸园区new出来的

2. 老年区

在新生区进行轻量GC之后,若是新生区满了,在进入老年区,若是老年区满了,则会进行重量GC。

3. 永久区(元空间)

(0)元空间:逻辑上存在:物理上不存在 (1)这个区域常驻内存的。 (2)用来存放JDK自身携带的Class对象。Interface元数据,存储的是Java运行时的一些环境或类信息 (3)这个区域不存在垃圾回收 (4)关闭VM虚拟就会释放这个区域的内存。 (5)一个启动类,加载了大量的第三方jar包。Tomcat部署了太多的应用,大量动态生成的反射类。不断的被加载。直到内存满,就会出现OOM;

十一、OOM解决方法及其内存调优

java.lang.OutOfMemoryError: Java heap space

OOM解决方法: (1)尝试扩大堆内存看结果

-Xms1024m -Xmx1024m -XX:+PrintGCDetails

(2)用能够看到代码是第几行出错的工具:内存快照分析工具。如:MAT,Jprofiler插件 MAT,Jprofiler作用:分析Dump内存文件,快速定位内存泄露;获得堆中的数据;

十二、Jprofiler的使用

  1. 假设现在有一个会报出OOM的错误代码如下,且在写代码时不知道抛出的时异常还是错误,而我们一般写的时异常,所以无法捕获到Error 在这里插入图片描述

  2. 为了测试,手动配置堆大小VM options:

-Xms1m -Xmx8m -XX:+HeapDumpOnOutOfMemoryError

其中: -Xms:设置初始化内存分配大小,默认为1/64 -Xmx:设置最大分配内存,默认 1/4 -XX:+PrintGCDetails 打印GC拉圾回收信息 -XX :+HeapDumpOnOutOfMemoryError 在这里插入图片描述

  1. 运行结果 在这里插入图片描述

  2. 找到该项目工程下的hprof文件,并且双击打开 在这里插入图片描述

  3. 首先看Biggest Objects,发现ArrayList占用了87%的堆,说明问题出在ArrayList上 在这里插入图片描述

  4. 其次接着看线程,确定问题出在哪一个线程,并且看到问题出在第21行 在这里插入图片描述

十三、GC

  1. GC一般在堆中进行
  2. GC分为轻量型和重量型
  3. GC大部分回收都在新生区里面的伊甸园区

十四、GC常用算法

1. 引用计数法

给每个堆中的对象设置一个计数器,每使用一次该对象,计数器加一,清楚计数为0的

2. 复制算法

在这里插入图片描述

3. 标记清除算法

在这里插入图片描述

部分资料参考B站狂神说Java