赞
踩
目标:
1.JVM是什么?
2.JVM内存怎么分区?按照什么方式分区?为什么要进行分区管理?
3.分区数据怎么管理?
JVM是一种规范,是JVM是虚拟机内存管理的规范和标准。Oracle公司提供了Java虚拟机规范文档,符合规范的语言都可以运行的JVM上。当然,用户也可以自己按照JVM的规范,设计自己的虚拟机,然后通过Oracle官方认证,就可以成为认可的JVM。
因此,JVM具有以下特性:
JVM跨平台型:可以运行的在不同的设备终端,包括Windows\Linux\Unix\Android\Mac等;
JVM语言无关性:遵循JVM规范的语言都可以运行在JVM上。例如Java\Scala\Kotlin\groovy等。
常见的JVM实现:
Java程序从编译到执行的过程
编译:使用javac 将Java程序编译为字节码文件,helloWorld.java --》helloWorld.class文件
加载:通过类加载器(ClassLoader)将字节码加载到方法区,
执行:然后交给JVM执行引擎进行进行。JVM执行引擎会把字节码文件翻译为 操作系统OS识别的机器码进行执行。
JDK是一个大的合集,包括JRE和JVM。包括编译工具javac、Java运行时环境JRE.
JRE:Java运行时环境。它包含了Java虚拟机(JVM)和Java核心类库,但不包括Java开发工具(如编译器和调试器)。这意味着JRE是Java程序运行时的最小环境,而JDK(Java开发工具包)则提供了开发、编译和调试Java应用程序所需的完整工具集。
JVM:Java虚拟机,主要负责类加载和执行,垃圾回收。
解释执行:字节码解释器,将class文件翻译为操作系统认识的机器码(一般是汇编程序或者二进制执行程序),交由操作系统执行。因为需要经过一次翻译,效率相对于直接编译为机器码的语言要低。
JIT执行:热点代码是指程序中的一些代码、方法,执行次数达到一定后(后端一般是1万+),作为热点代码。JIT编译器会将Java代码(class代码)直接翻译为机器码执行,提高效率。翻译面板存在于JVM的Codecache。Oracle HotSpot虚拟机支持JIT。
什么是运行是数据区?Java虚拟机在执行Java程序的过程中会把它所管理的内存(JVM)划分不同的数据区域。
运行时数据库采用的是JVM内存分区管理的机制。分区管理使内存管理更加规范化。
运行时数据区按照功能划分为:线程共享(方法区和堆)和线程私有(虚拟机栈、本地方法栈、程序计数器)
方法区:方法区主要存储了类的结构信息(Clazz)、运行时常量、静态变量, HotSpot中称为永久代、元空间(MataSpace)。
堆:存储对象实体,JVM中空间最大的一部分。堆是容易发生内存溢出的主要分区。
虚拟机栈:Java 虚拟机栈的生命周期和线程相同,Java 虚拟机栈描述的是 Java 方法执行的。Java程序的执行对应的一系列方法的调用,一个方法对应栈中一个栈帧,因此虚拟机栈也称为方法栈。
本地方法栈:Java本地方法(Native调用的方法,如C++/C程序)执行的栈。Native方法的执行在本地方法栈中执行(也是方法栈帧的方式)。
程序计数器:是一个较小的内存空间,用于存储指向下一条指令的地址。程序计数器分区永远不会发生内存溢出。
进程中还有一部分,没有经过JVM进行虚拟化的部分--直接内存,不属于 JVM统一管理。
用于存储当前线程运行Java方法所需的数据、指令、返回地址。
默认1M大小
栈内存不足时会发生栈溢出。栈溢出常见的两种情况:
方法调用死循环:方法调用出现死循环,一直不停的创建栈帧,直到栈溢出
创建大对象:创建的栈对象超出了最大栈空间大小。例如栈限制大小2M,创建一个3M的栈数组。
Java执行方法时,会生成一个栈帧,压入虚拟机栈。
线程执行方法 main-->A()-->B(),执行时栈内存中情况
栈帧的管理先进后出的规则(栈存储的特点)
简单的程序演示java程序运行流程
我们看work方法的执行过程
1) woke方法栈帧入栈
方法区work方法的代码,创建一个栈帧,用于work方法的执行,并且将栈帧压入虚拟机栈。
字节码左侧数字: 字节码地址,字节码指令在work方法中的偏移量,
字节码指令:数字后面是字节码指令
2) 执行第一条指令 iconst_1: 程序计数器修改指令地址为0,执行0号指令。
创建一个int类型的常量,值为1,
3)执行第1号指令 istore_1: 将操作数栈中的数据弹出来(POP出栈),放入局部变量表 1 号位置。程序计数器指向方法 1 位置。
istore是写入一个整数值。
4)同样的方法执行指令2号指令,程序计数器变为2,创建一个整型常量,值为2,压入操作数栈;
执行指令3号指令,程序计数器变为3,操作数栈数据出栈,存入局部变量表 2 号位置。
5)执行第4号指令,程序计数器变为4,iload_1指令,把局部变量表下标为1的整数,压入操作数栈;
执行5号指令,将局部变量表2号位置的整数压入操作数栈,程序计数器变为5,操作数栈压入局部表量表2号位置的整数,值为2,位于操作数栈栈顶。
6)指令6号指令,程序计数器变为6,执行指令iadd, 先把两个操作数分别出栈;
然后计算,2+1=3,
将计算结果重新压入操作数栈
7) 执行7号指令,bipush 10,将整数10 压入操作数栈,这个是双字节指令,程序计数器变为7
8)执行9号指令,程序计数器变为9,imul两个整数相乘,操作数栈中的两个整数分别出栈,计算得到结果:10*3=30;将计算结果重新压入操作数栈。
9)执行第10号指令,程序计数器变为10,istore_3, 将操作数栈中的整数出栈,存入局部变量表3号位置。
10)执行11号指令,程序计数器变为10,iload_3,将局部变量表3号位置的整型数(30),加载到操作数栈中。
11)执行指令12号,程序计数器变为12,ireturn, 操作数栈中的整型数(30)出栈,work栈帧出栈,返回main-栈帧,返回值30,返回位置在完成出口记录(main方法的3号位置),在main方法
好位置继续接着执行。work方法栈帧结束生命周期,释放work栈帧内存。
Java直接操作不了线程,需要通过本地方法,调用线程操作。运行到本地方法时,因为不是字节码程序,不能得到对应的指令地址,程序计数器置位null
JHSDB工具,位于SDK/lib/sa-jdi.jar
启动方法:
java -cp [sa-jdi.jar路径] sun.jvm.hotspot.HSDB
1) attach需要查看的进程
2)查看堆的话,能够看到对内存分区的物理地址范围
Java新生代进阶老年代,15岁进阶老年代,因为表示年龄的4个bit位,1111,最大为15.
1.JVM为什么需要采用分区管理模式?
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。