JVM的学习从这里开始——JVM内存结构

为什么要了解虚拟机

JVM不单单只支持Java语言,也支持其他语言(Scala、Kotlin、Groovy等等)
区块链2.0–以太坊(比特币是区块链1.0) 中提供了EVM的虚拟机,它的实现和JVM类似,基于栈、生成脚本编译成字节码来执行。知识通用。(理论大于实际)

虚拟机历史

解释执行和编译执行(针对字节码的执行)

  • 解释执行就是边翻译为机器码边执行。
  • 编译执行(即时编译)就是先将一个方法中的所有字节码全部编译成机器码之后再执行。

Hotspot采用的是先解释执行,到了一定时机后热点代码(多次执行、循环等)再翻译成机器码(热点代码探测技术:通过执行计数器找到最有编译价值的代码,如果代码用得非常频繁,就会把这些代码编译成本地代码)。
在这里插入图片描述
JRockit采取的方法是在执行class时直接编译为机器码(Java程序启动速度会比较慢,JVM无法获得程序的运行时信息)。

J9和Hotspot比较接近,主要是用在IBM产品(IBM WebSphere和IBM的AIX平台上),华为有的项目用的J9。

谷歌:Google Android Dalivk VM:使用的寄存器架构,执行dex(Dalvik Executable)通过class转化而来。

未来的Java技术

在这里插入图片描述
动态化模块化:OSGI(Open Service Gateway Initiative),应用层面就是微服务,互联网的发展方向

混合语言:多个语言都可以运行在JVM中,google的Kotlin 成为了 Android 的官方语言。Scala(Kafka)

多核并行:CPU从高频次转变为多核心,多核时代。JDK1.7引入了Fork/Join,JDK1.8提出lambda表达式(函数式编程天生适合并行运行)

丰富语法:JDK5提出自动装箱、泛型(并发编程讲到)、动态注解等语法。JDK7二进制原生支持。try-catch-finally 至try-with-resource

64位:虽然同样的程序64位内存消耗比32位要多一点,但是支持内存大,所以虚拟机都会完全过渡到64位,32位的JVM有4G的堆大小限制。

更强的垃圾回收器(现在主流CMS、G1):JDK11 –ZGC(暂停时间不超过10毫秒,且不会随着堆的增加而增加,TB级别的堆回收)):有色指针、加载屏障。JDK12支持并发类卸载,进一步缩短暂停时间 JDK13(计划于2019年9月)将最大堆大小从4TB增加到16TB

Java SE体系架构

JavaSE,Java平台标准版,为Java EE和Java ME提供了基础。
在这里插入图片描述
JDK:Java开发工具包,JDK是JRE的超集,包含JRE中的所有内容,以及开发程序所需的编译器和调试程序等工具。
JRE:Java SE运行时环境 ,提供库、Java虚拟机和其他组件来运行用Java编程语言编写的程序。主要类库,包括:程序部署发布、用户界面工具类、继承库、其他基础库,语言和工具基础库。
JVM:Java虚拟机,负责JavaSE平台的硬件和操作系统无关性、编译执行代码(字节码)和平台安全性。

运行时数据区域

JVM在运行过程中会把它所管理的内存划分成若干不同的数据区域。

  • 线程私有:程序计数器、虚拟机栈、本地方法栈
  • 线程共享:堆、方法区
    在这里插入图片描述

线程私有

程序计数器

较小的内存空间,当前线程执行的字节码的行号指示器;各线程之间独立存储,互不影响。
作用:确保多线程情况下的程序正常执行

  • 如果线程正在执行的是一个Java方法,则指明当前线程执行的字节码行数
  • 如果正在执行的是Natvie方法,这个计数器值则为空(Undefined)

此内存区域是唯一一个不会出现OutOfMemoryError情况的区域。

虚拟机栈

缺省大小为1M,调整参数:-Xss大小。
在这里插入图片描述
虚拟机栈是每个线程所私有的。线程在运行时,在执行每个方法的时候都会打包成一个栈帧,存储了局部变量表,操作数栈,动态链接,方法出口等信息,然后放入栈。每个时刻正在执行的当前方法就是虚拟机栈顶的栈桢。方法的执行就对应着栈帧在虚拟机栈中入栈和出栈的过程。

  • 局部变量表:用于存放局部变量的。首先它是一个32位的长度,主要存放我们的Java的八大基本数据类型,一般32位就可以存放下,如果是64位的就使用高低位占用两个也可以存放下,如果是局部的一些对象,比如我们的Object对象,我们只需要存放它的一个引用地址即可。
  • 操作数栈:存放我们方法执行的操作数的,操作的元素可以是任意的Java数据类型。一个方法刚刚开始的时候,这个方法的操作数栈是空的。运行方法时,操作数栈会一直运行入栈/出栈的操作。
  • 动态连接:Java语言特性多态,需要类加载、运行时才能确定具体的方法。
  • 返回地址:正常返回(调用程序计数器中的地址作为返回)三步曲:
    1. 恢复上层方法的局部变量表和操作数栈、
    2. 把返回值(如果有的话)压入调用者栈帧的操作数栈中、
    3. 调整PC计数器的值以指向方法调用指令后面的一条指令、
      异常的话,通过异常处理器表(非栈帧中的)来确定。

本地方法栈

本地方法栈保存的是native方法的信息,当一个JVM创建的线程调用native方法后,JVM不再为其在虚拟机栈中创建栈帧,JVM只是简单地动态链接并直接调用native方法。
各虚拟机自由实现,本地方法栈native方法调用 JNI(Java Native Interface)到了底层的C/C++触发汇编语言,然后驱动硬件。

线程共享

几乎所有对象都分配在这里,也是垃圾回收发生的主要区域,可用以下参数调整:
-Xms:堆的最小值;
-Xmx:堆的最大值;
-Xmn:新生代的大小;
-XX:NewSize:新生代最小值;
-XX:MaxNewSize:新生代最大值;
-XX:+PrintGCDetails:打印垃圾回收日志,程序退出时输出当前内存的分配情况。

静态常量吃、运行时常量池、字符串常量池逻辑上属于方法区,但物理上属于堆

方法区(永久代(JDK1.7和以前)、元空间(JDK1.8))

在Java虚拟机中,方法区( method area)是可供各个线程共享的运行时内存区域。方法区存储了每一个类的结构信息,例如,运行时常量池(runtime constant pool)、字段和方法数据、构造函数和普通方法的字节码内容,还包括一些在类、实例、接口初始化时用到的特殊方法。

方法区在虚拟机启动的时候创建,虽然方法区是堆的逻辑组成部分,但是简单的虚拟机实现可以选择在这个区域不实现垃圾收集与压缩。这个版本的Java虚拟机规范也不限定实现方法区的内存位置和编译代码的管理策略。方法区的容量可以是固定的,也可以随着程序执行的需求动态扩展,并在不需要过多空间时自动收缩。方法区在实际内存空间中可以是不连续的。

元空间是对JVM规范中方法区的实现。元空间并不在虚拟机中,而是使用本地(直接)内存。因此,默认情况下,元空间的大小仅受本地内存限制,可以通过参数来指定元空间的大小。

符号引用

一个Java类A被编译成一个class文件时,如果A引用了类B,但是在编译时A类并不知道B的实际内存地址,因此只能使用符号引用来代替。而在类装载器装载A时,此时可以通过虚拟机获取B的实际内存地址,因此便可以将符号替换为B的实际内存地址,及直接引用地址。

简而言之,在编译时用符号引用来代替引用类,在加载时再通过虚拟机获取该引用类的实际地址.
以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义地定位到目标即可。符号引用与虚拟机实现的内存布局是无关的,引用的目标不一定已经加载到内存中。

字面量

字符串 String a = “abc”,这个abc就是字面量
八种基本类型 int a = 1; 这个1就是字面量
声明为final的常量。

静态常量池(Class 常量池)

在 Class 文件中除了有类的版本、字段、方法和接口等描述信息外,还有一项信息是常量池表 (Constant Pool Table),用于存放编译期间生成的各种字面量符号引用

运行时常量池

  • JDK1.6
    运行时常量池在方法区中
  • JDK1.7
    运行时常量池在堆中
  • JDK1.8
    运行时常量池逻辑上在方法区中,物理上在堆中

运行时常量池(Runtime Constant Pool)是每一个 Class 常量池(Constant Pool)的运行时表示形式,它包括了若干种不同的常量:从编译期可知的数值字面量到必须运行期解析后才能获得的方法或字段引用

运行时常量池是在类加载完成之后,将 Class 常量池中的符号引用值转存到运行时常量池中,类在解析之后,将符号引用替换成直接引用

字符串常量池

相关见 关于 String、StringBuffer、StringBuilder 的常见面试题

直接内存

JVM规范里没有规定的,在NIO中经常用到。可以参考:

NIO基础(一)之Buffer
在这里插入图片描述
不是虚拟机运行时数据区的一部分,也不是java虚拟机规范中定义的内存区域。

  • 如果使用了NIO,这块区域会被频繁使用,在java堆内可以用directByteBuffer对象直接引用并操作。
  • 这块内存不受java堆大小限制,但受本机总内存的限制,可以通过MaxDirectMemorySize来设置(默认与堆内存最大值一样),所以也会出现OOM异常。
  • 避免了在Java 堆和Native 堆中来回复制数据,能够提高效率。
©️2020 CSDN 皮肤主题: 岁月 设计师: pinMode 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值