• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

Java虚拟机JVM知识和面试考点01

武飞扬头像
去见小熊
帮助1

简介

Java虚拟机(Java Virtual Machine,JVM)是Java平台的核心组成部分之一,它是一个在计算机上运行Java字节码的虚拟机。JVM充当了Java应用程序和底层操作系统之间的中间层,提供了跨平台的特性,使得Java程序可以在不同的操作系统和硬件上运行。
注意:JVM是运行在操作系统之上的,它与硬件没有直接的交互。

这第一篇文章我们描述JVM的主要结构及面试常考点,第二篇主要描述JVM的垃圾回收相关。

JVM结构

结构简介

  • 方法区:存储已被虚拟机加载的类元数据信息(元空间)
  • 堆:存放对象实例,几乎所有的对象实例都在这里分配内存
  • 虚拟机栈:虚拟机栈描述的是Java方法执行的内存模型,每个方法被执行的时候都会同时创建一个栈帧(Stack Frame)用于存储局部变量表、操作栈、动态链接、方法出口等信息
  • 程序计数器:当前线程所执行的字节码的行号指示器
  • 本地方法栈:本地方法栈则是为虚拟机使用到的Native方法服务学新通

类加载器ClassLoader

类加载器负责加载class文件,class文件在文件开头有特定的文件标示,并且ClassLoader只负责class文件的加载,至于它是否可以运行,则由Execution Engine决定。

类加载器的种类

类加载器分为四种:前三种为虚拟机自带的加载器。

  • 启动类加载器(Bootstrap)负责加载$JAVA_HOME中jre/lib/rt.jar里所有的class,由C 实现,不是ClassLoader子类
  • 扩展类加载器(Extension)负责加载java平台中扩展功能的一些jar包,包括、$JAVA_HOME中jre/lib/*.jar或-Djava.ext.dirs指定目录下的jar包,由Java实现的。
  • 应用程序类加载器(AppClassLoader)Java 也叫系统类加载器,负责加载classpath中指定的jar包及目录中class
  • 用户自定义加载器 Java.lang.ClassLoader的子类,用户可以定制类的加载方式

类加载器工作过程

  • 1、当AppClassLoader加载一个class时,它首先不会自己去尝试加载这个类,而是把类加载请求委派给父类加载器ExtClassLoader去完成。
  • 2当ExtClassLoader加载一个class时,它首先也不会自己去尝试加载这个类,而是把类加载请求委派给BootStrapClassLoader去完成。
  • 3、如果BootStrapClassLoader加载失败(例如在$JAVA_HOME/jre/lib里未查找到该class),会使用ExtClassLoader来尝试加载;
  • 4若ExtClassLoader也加载失败,则会使用AppClassLoader来加载
  • 5、如果AppClassLoader也加载失败,则会报出异常ClassNotFoundException

其实这就是所谓的双亲委派模型。简单来说:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把请求委托给父加载器去完成,依次向上。示例图如下:

好处:防止内存中出现多份同样的字节码(安全性角度),可以保证jdk的稳定性和安全性。

学新通

执行引擎Execution Engine

Execution Engine执行引擎负责解释命令,提交操作系统执行(把字节码解析成具体指令并提交操作系统执行)。

本地方法接口

调用系统类库的一些接口

运行时数据区

方法区(线程共享的)

  • 线程共享的
  • 保存方法信息、常量、接口信息等
  • jdk1.7之前:使用永久代实现了方法区,常量池就在永久代
  • jdk1.7 逐步去永久代,常量池在堆中
  • jdk1.8 没有永久代,使用元空间实现了方法区,常量池就在元空间

堆(线程共享的)

Heap 堆:一个JVM实例只存在一个堆内存,堆内存的大小是可以调节的。类加载器读取了类文件后,需要把类、方法、常变量放到堆内存中,保存所有引用类型的真实信息,以方便执行器执行,堆内存逻辑上分为三部分:

  • Young Generation Space 新生区 Young/New
  • Tenure generation space 养老区 Old/Tenure
  • Permanent Space 永久区 Perm

也称为:新生代(年轻代)、老年代、永久代(持久代)。 其中JVM堆分为新生代和老年代

学新通

新生区/代(年轻代)

新生区是对象的诞生、成长、消亡的区域,一个对象在这里产生,应用,最后被垃圾回收器收集,结束生命。新生区又分为两部分: 伊甸区(Eden space)和幸存者区(Survivor pace) ,所有的对象都是在伊甸区被new出来的。幸存区有两个: 0区(Survivor 0 space)和1区(Survivor 1 space)。当伊甸园的空间用完时,程序又需要创建对象,JVM的垃圾回收器将对伊甸园区进行垃圾回收(Minor GC),将伊甸园区中的不再被其他对象所引用的对象进行销毁。然后将伊甸园中的剩余对象移动到幸存 0区。若幸存 0区也满了,再对该区进行垃圾回收,然后移动到 1 区。那如果1 区也满了呢?再次垃圾回收,满足条件后再移动到养老区。若养老区也满了,那么这个时候将产生MajorGC(FullGC) ,进行养老区的内存清理。若养老区执行了Full GC之后发现依然无法进行对象的保存,就会产生OOM异常“OutOfMemoryError”。

如果出现java.lang.OutOfMemoryError: Java heap space异常,说明Java虚拟机的堆内存不够。原因有二:

(1)Java虚拟机的堆内存设置不够,可以通过参数-Xms、-Xmx来调整。

(2)代码中创建了大量大对象,并且长时间不能被垃圾收集器收集(存在被引用)。

总结
eden(新创建对象) from(幸存对象) to(总是空的),会发送minorGC/YoungGC。

  1. 项目启动时:eden from to都是空的。
  2. 项目在处理请求的过程中,会反复创建对象,这些对象会保存在eden。
  3. eden如果满了,再去创建对象,此时就会引发minorGC,会把幸存对象保存在from,再去清空eden。
  4. 接下来又会向eden中反复创建对象,eden满了,如果还要创建对象,就会引发minorGC(eden 和 from)把幸存对象保存到to中清空eden和from,最后from和to会交换位置。
  5. 反复执行第4步。
  6. 经过15次GC(默认)依然存活的对象会移到养老区。
养老区(老年代)
  • 整个堆中最大的一块区域,存放两类对象:15GC依然存活的对象或者大对象。
  • FullGC、MajorGC:每次majarGC会伴随着一次minorGC。两次MajarGC之后依然无法保存新创建的对象,就会触发OOM:java heap space原因:堆内存太小(-Xms -Xmx) 在程序中创建了大量的大对象。
  • 老年代的对象比较稳定,不会频繁的GC
永久区(永久代,Non-Heap非堆)

永久存储区是一个常驻内存区域,用于存放JDK自身所携带的 Class,Interface 的元数据,也就是说它存储的是运行环境必须的类信息,被装载进此区域的数据是不会被垃圾回收器回收掉的,关闭 JVM 才会释放此区域所占用的内存。

  • 就是方法区,存放方法 类 接口 常量信息,可能发生OOM:permgen space OOM原因:
  1. 引入了大量的第三方jar包,或者项目本身太大
  2. 永久代设置的太小了
  3. 一个tomcat放入了多个项目
  4. 程序中大量使用了反射
  • JDK更新变化
  1. Jdk1.6及之前: 有永久代,常量池1.6在方法区
  2. Jdk1.7: 有永久代,但已经逐步“去永久代”,常量池1.7在堆
  3. Jdk1.8及之后: 无永久代,常量池1.8在元空间(Metaspace)

实际而言,方法区(Method Area)和堆一样,是各个线程共享的内存区域,它用于存储虚拟机加载的:类信息 普通常量 静态常量 编译器编译后的代码等等,虽然JVM规范将方法区描述为堆的一个逻辑部分,但它却还有一个别名叫做Non-Heap(非堆),目的就是要和堆分开。

对于HotSpot虚拟机,很多开发者习惯将方法区称之为“永久代(Parmanent Gen)” ,但严格本质上说两者不同,或者说使用永久代来实现方法区而已,永久代是方法区(相当于是一个接口interface)的一个实现,jdk1.7的版本中,已经将原本放在永久代的字符串常量池移走。

常量池(Constant Pool)是方法区的一部分,Class文件除了有类的版本、字段、方法、接口等描述信息外,还有一项信息就是常量池,这部分内容将在类加载后进入方法区的运行时常量池中存放。

jvm栈(线程私有的)

栈也叫栈内存,主管Java程序的运行,是在线程创建时创建,它的生命期是跟随线程的生命期,线程结束栈内存也就释放,对于栈来说不存在垃圾回收问题,只要线程一结束该栈就Over,生命周期和线程一致,是线程私有的。8种基本类型的变量 对象的引用变量 实例方法都是在函数的栈内存中分配。(第二篇讲JVM垃圾回收机制) 栈中的数据都是以栈帧(Stack Frame)的格式存在,栈帧是一个内存区块,是一个数据集,是一个有关方法(Method)和运行期数据的数据集。

栈帧中主要保存3 类数据:

  • 本地变量(Local Variables):输入参数和输出参数以及方法内的变量(本地变量,8种基本数据的变量和对象的引用地址)。
  • 栈操作(Operand Stack):记录出栈、入栈的操作(出栈或者入栈信息)。
  • 栈帧数据(Frame Data):包括类文件、方法等等(方法的定义信息)。

遵循“先进后出”或者“后进先出”原则。

学新通 不会产生OOM,产生StackOverflowError,在递归调用时,可能产生内存溢出

PC寄存器(线程私有的)

  • 本质就是一个指针,指向该线程将要执行的方法区中的下一行指令。大小几乎可以忽略不计
  • 指向当前线程将要执行的方法区中的代码指令

本地方法栈

保存native类型方法的一块区域

JVM参数调优

常用JVM参数

  • -Xmn 新生代大小
  • -Xms 初始堆大小
  • -Xmx 最大扩展的堆大小
  • -XX: PrintGCDetails 输出堆及GC的具体信息

调优工具

  • eclipse中的MAT插件
  • idea或者命令行使用jdk自带工具 jdk/bin/jvisualvm.exe

举例idea分析dump文件

  1. 修改程序启动参数为:-Xmx10m -Xms5m -XX: HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D:\test
    -XX:HeapDumpPath:生成dump文件路径。(一定要存在)
  2. 编写测试程序

学新通

学新通 3. 会生成hprof文件

学新通

  1. jdk自带了该类型文件的解读工具:jvisualvm.exe(在jdk的bin目录下)

学新通

  1. 双击打开

学新通 6. 导入生成的hprof文件(注意后缀)

学新通

学新通

调优指令

  • jps -l查看正在运行的java进程
  • jinfo -flags 进程号 查看当前java进程的jvm参数
  • jstat -gc 进程号 查看GC统计信息

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgahfia
系列文章
更多 icon
同类精品
更多 icon
继续加载