什么是JVM?
JVM:Java虚拟机(英语:Java Virtual Machine,缩写:JVM),一种能够执行Java字节码的虚拟机,以堆栈结构机器来实现。最早由Sun微系统所研发并实现第一个实现版本,是Java平台的一部分,能够执行以Java语言写作的软件程序。
通过维基百科的解释,可以了解到JVM虚拟机,是Java程序运行的基座。Java程序需要在JVM上才可以运行。可以理解为Java程序是在JVM上运行,而不去和底层系统进行交互。JVM的作用是将代码转化为机器能理解的语言,再由机器去执行代码。
JVM的构成有哪些?
JVM主要由运行数据区、执行引擎、本地方法区、本地方法库、类机载器,这五个部分组成。
执行引擎(execution Engine):将字节码指令解释/编译为对应平台上的本地机器指令。
本地方法接口(Java Native Interface):调用其他语言写的方法。
本地方法库:字面意思,可以理解为给本地方法接口提供的操作库。
类加载器(Class Loader):类加载器内容很多,独立章节讲
运行时内存空间(Runtime Data Area):JVM关键内容,独立章节讲
运行时内存空间
什么是运行时内存空间?
指的是程序运行的时候,JVM的内存组成。不同区域的内存空间负责不同的功能。
运行时内存空间有哪些部分组成?
· 程序计数器(Program Counter Register):
· 线程是没有记忆的,一旦线程被阻塞后再唤醒就不知道自己要做什么,程序计数器用于记录线程执行到哪条指令。
· 保存当前线程所正在执行的字节码指令的地址
· 为了在线程切换之后能恢复到正确的执行位置,每个线程都有独立的程序计数器
· 程序计数器内存区域是虚拟机中唯一没有规定OOM情况的区域
· 虚拟机栈(VM Stack):
· 是线程私有的内存,生命周期和线程相同,方法在执行的时候,都会在虚拟机栈中创建一个栈帧(Stack Frame),用于存储局部变量表、操作数栈,动态链接、方法出口等信息。
· 表示方法运行时候,在JVM中的内存模型,最小单位是栈帧。存储存储局部变量表、操作数栈,动态链接、方法出口。
· 局部变量表:存储临时的8个基本数据类型、对象引用地址、returnAddress(保存return后要执行的字节码的指令地址)类型。
· 局部变量表在class文件中已经定义好最大大小
· 局部变量表的容量以变量槽(Slot)为最小单位,32位的虚拟机中一个Slot可以存放32位以内的数据。
· 对于64位长度的数据类型(long double),虚拟机会以高位对齐的方式为其分配两个连续的Slot空间。
· Slot是可以重用的,当Slot中的变量超出了作用域,那么下一次分配Slot的时候,会覆盖原来的数据,Slot对对象的引用会影响GC(如果被引用,那么将不会被回收)
· 系统不会为局部变量赋予初始值。
· 操作数栈
· 数据操作区域,作为数据临时变更的空间。比如代码有i=66+1,66+1这个操作在操作数栈中计算,然后存入局部变量表中。
· 在编译时期已经确定最大大小
· 动态链接
· 存储调用别的方法的地址信息。因为在class文件中,所有的变量和方法引用都是作为符号引用,保存在class文件的常量池中,所以动态链接的作用是将这些符号引用转化为直接引用。比如方法A中要调用方法B,class文件中,写的是B方法名称,在动态链接中存储方法B的名称和地址对应关系。
· 方法返回地址
· 字面意思,有两种返回,正常返回和异常抛出。
· 本地方法栈(Nactive Method Stack):
· 和虚拟机栈的方法类似,只不过服务于Navite方法。
· Java堆(heap):
Java虚拟机中内存最大的一块,是被所有线程共享的,几乎所有的对象实例都在这里分配内存。
· 堆的特点:
· 先进先出
· 是JVM中占用空间最大的内存,并且运行时动态分配内存大小
· 所有线程共享的内存
· GC的主要场所
· JVM启动的时候创建堆
· 逻辑上连续的,物理上非连续(内存地址不连续)
· 堆的构成
· PermGen Space:永久代。永久保存的区域,用于存放Class和Meta信息,Class在被Load的时候放入该区域,GC不会对PermGen Space进行清理,所以如果有很多的Class被load,那么会跑出OOM异常。JDK1.8之后不存在永久代,改为元空间,也不需要进行内存分配,元数据直接使用系统内存,而不是JVM内存。
· Young Space:新生代对象,保存刚实例化的对象。当该区被填满时,GC会将对象移到Old Space中。
· 当中又被分为Eden区、From Survivor区、To Survivor区,存在两个Survivor区域是因为GC要进行复制算法,所以需要两块区域。由于有Survivor区的存在,Eden区不会直接向老年代直接传送对象,减少Full GC的发生,
· Eden区、From Survivor区、To Survivor区的默认内存比例为8:1:1。
· Old Space:老年代的对象,在多次Minor GC之后,依旧存在对象会被转移到年老代,老年代的内存空间应该要比年轻代大。与Young Space的内存空间比为1:2。
· 堆内存分配
· 最开的内存大小由Xms指定,默认是物理内存的1/64
· 最大的内存由Xmx指定,默认是物理内存的1/4
· 堆内存空间小于40%的时候,JVM会增加堆内存大小
· 堆内存空间大于70%的时候,JVM会减少堆内存的大小
· 一般将Xms和Xmx设置为同一个值,禁止JVM划分堆内存,优化性能
· 非堆内存分配(永久代内存分配)
· 使用-XX:PermSize设置非堆内存的初始值,默认是物理内存的1/64
· 使用-XX:MaxPermSize设置非堆内存的最大值,默认是物理内存的1/4
· 垃圾回收机制
· MinorGC:清理年轻代的内存空间,垃圾回收过程是(复制->清空->互换),采用的算法是复制算法。
· 过程:当Eden区满的时候,释放在Eden中所有不活跃的对象,活跃的对象放入Survivor区域,并且对象的年龄+1,大对象会直接进入老年区(-XX:PretenureSizeThreshold参数可以设置多大的对象可以直接进入老年区)。如果Survivor区域已经满了,那么会对Survivor进行MinorGC,将年龄达到设置值(默认15)的对象放入老年区。
· MajorGC:老年代内存不足时,触发老年代GC。采用标记清除法或者标记整理算法的混合实现,该GC不会有MinorGC那么频繁,并且一次MajorGC要比MinorGC时间更长。
· FullGC:清理整个堆空间
· 方法区(Method Area):
· 所有线程共享的,用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译后的代码等数据。
· 直接内存
· 在JDK1.4中引入NIO之后,为了视线一种通过native函数直接分配对外内存的,这一切是通过两个概念实现的:channel和buffer。会出现OOM的情况,是受物理机器的内存限制的。
· 直接内存并不受JVM内存回收管理
PS
Q:Java程序是不是只能运行在JVM上?
A:!!!!!---!!!!!从狭义上讲,JVM主要的任务是将class文件翻译为机器码。只要能翻译class文件变为机器码,那就不需要JVM来运行class文件。从广义上讲,能翻译class文件为机器码的东西,可以称之为JVM。
Q:JDK、JRE和JVM的区别?
A:JDK是 Java 的开发工具包,提供了 Java 应用程序开发所需的工具和库。JDK 包括 Java 编译器(javac)、Java 虚拟机(JVM)和 Java 库等组件。JDK 可以用于开发 Java 应用程序、Java Servlet 和 Java Server Pages(JSP)等服务器端应用程序,以及 Java 应用程序的桌面版本等。
JRE是 Java 的运行环境,是 Java 应用程序运行的基本环境。JRE 包括 Java 虚拟机(JVM)、Java 标准库和其他组件。JRE 只能用于运行 Java 应用程序,不能用于开发 Java 应用程序。
JVM是 Java 虚拟机,是 Java 应用程序的运行时环境,可以在不同的操作系统上运行 Java 应用程序。JVM 实现了 Java 字节码的解释和执行,并提供了内存管理、垃圾回收等机制。JVM 是跨平台的,可以在不同的硬件平台和操作系统上运行。