美文网首页Java
JVM学习笔记

JVM学习笔记

作者: 有只怪好强 | 来源:发表于2019-10-07 17:11 被阅读0次

    一、JVM简介

    1.1一次编译,到处运行

    java有个很突出的特性就是跨平台,只需要编译一次,就能在不同的操作系统、不同的平台上运行,也就是所谓的“一次编译,到处运行”。事实上,这个功能和java虚拟机是密不可分的。Java源文件,通过编译器,能够生产相应的.class文件,也就是字节码文件,而字节码文件又通过Java虚拟机中的解释器,编译成特定机器上的机器码,每一种平台的解释器是不同的,但是实现的虚拟机是相同的,这也就是Java为什么能够跨平台的原因。


    一次编译,到处运行.jpg

    1.2JVM概念

    JVM是Java Virtual Machine的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。
    简单来说JVM是可运行Java代码的假想计算机

    1.3JVM体系结构

    jvm体系结构.jpg

    .class文件被class loader加载,由执行引擎来具体执行包在装载类的方法中的指令,也就是方法,而运行时数据区则是从整个计算机内存中开辟一块内存存储Jvm需要用到的对象,变量等,运行区数据有分很多小区,分别为:方法区,虚拟机栈,本地方法栈,堆,程序计数器,这个后文会详细说明。
    这里需要说明的是,在运行时数据区,方法区和堆是被线程共享的数据区,而java栈、本地方法栈、程序计数器则是线程私有的。

    2.运行时数据区详细说明

    2.1方法区

    方法区是可供各线程共享的运行时内存区域,主要存放有已被加载的类的元数据信息、静态变量、即时编译器编译后的代码等,另外在JDK1.6及之前,这里还会有运行时常量池,在1.7以后常量池以挪到堆中。

    方法区、永久代、元空间

    这里有三个相关但不同的概念,方法区在上面已经解释了,而永久代可以认为是方法区在HotSpot的具体实现。永久代的垃圾回收是和老年代的垃圾回收绑定的,一旦一个区域被占满,这两个区域都要进行垃圾回收,由于类及方法的信息等比较难确定其大小,对于永久代的调优是困难的,而且也给GC带来了不必要的复杂度。

    在jdk1.8后,永久代被移动到了一个跟堆完全不相连的本地内存区域,也就是元空间,也就是说元空间的最大可分配空间就是系统可用内存空间。因此,我们就不会遇到永久代存在时的内存溢出错误。

    2.2 堆

    对于绝大多数的应用来说,这块区域是JVM所管理的内存中最大的一块,也是线程共享的区域,主要用于存放对象实例和数组,内部会划分出多个线程私有的分配缓冲区(TLAB)。

    2.3 JAVA栈

    java的虚拟机栈是线程私有的,其生命周期和线程一致。每个方法在执行时都会创建一个栈桢被压入栈中,栈桢中的数据主要包括以下三类:

    本地变量(Local Variables):输入参数和输出参数以及方法内的变量;
    栈操作(Operand Stack):记录出栈、入栈的操作;
    栈帧数据(Frame Data):包括类文件、方法等等。

    每个方法从被调用到执行完,对应着一个栈桢在虚拟机中入栈到出栈的过程,举例说明,当方法A被调用时,就往栈中压入栈桢F1,如果A又调用了B方法,则产生的栈桢F2也被压入,执行完毕后,先弹出F2再弹出F1。

    2.4 本地方法栈

    和JAVA栈不同,本地方法栈为虚拟机执行本地方法服务。

    2.5 程序计数器

    线程私有,就是一个指向方法区中下一个将要执行的指令代码的指针,由执行引擎读取下一条指令,是一个非常小的内存空间,几乎可以忽略不计。

    3 HotSpot虚拟机中对象的相关知识总结

    3.1 对象的内存分配布局

    在hotspot虚拟机中,对象主要分为三部分:对象头、实例信息以及对其填充,如下图所示:


    Hotspot虚拟机对象内存分部.jpg
    对象头

    对象头主要包含两部分,一部分是对象自身运行时所需要的数据,例如哈希码、GC年代信息、锁状态标志、偏向线程id,线程持有的锁等等;第二部分是类型指针,指向它的类的元数据。如果是数组类型,对象头中还会有一部分用来记录数组的长度

    实例数据

    程序代码中所定义的各种类型的字段内容(包含父类继承下来的和子类中定义的)

    对齐填充

    主要是为了确保对象的大小是字节的整数倍的占位,并非必须

    3.2 如何访问对象

    在访问(使用)对象时,会用到java栈上本地变量表里面的reference数据来操作,具体有通过句柄访问和直接访问两种方式。

    3.2.1 通过句柄访问
    通过句柄访问对象.jpg
    3.2.2 直接访问

    由于对象头上有类型指针,所以访问时看也可以通过类型指针直接访问,如图所示

    指针直接访问对象.jpg

    4 垃圾回收

    4.1 什么叫对象不可达

    对于java而言,不可用不可达的对象,将会被GC回收掉,那什么是对象不可达呢?
    所谓的对象不可达,是指通过一系列的“GC Roots”的对象作为起点,从这些起点,从这些节点出发所走过的路径称为引用链。当一个对象到任何GC Root都没有任何引用链时,则称为对象不可达,如下图所示:


    对象不可达.jpg

    可以作为GC Root的对象有:
    1.栈桢中本地变量表里面的引用对象
    2.方法区类的静态属性引用的对象
    3.方法区常量引用对象
    4.本地方法栈中JNI引用的对象

    4.2 垃圾回收算法

    一般有以下几种
    1 标记——清除算法
    标记后直接清除。缺点是效率不高、且容易产生大量空间碎片

    2.复制算法
    把空间分成两块,每次对其中一块GC。即当一块的内存使用完时,把存活对象copy到另一块上。缺点是空间利用率不高。

    一般新生代的GC是采用的该算法,因为大多数新生代对象都熬不过第一次GC,所以没必要按1:1进行分区。一般是一块较大的Eden区和两块较小的Survivor区,比例大致是8:1:1,当GC时,将Eden区和其中一块Survivor区的存活对象copy到另一块Survivor区,超出部分直接进入老年代,然后清理Eden和Survivor区。

    3.标记——整理算法
    把存活的对象移动到内存的一端,然后对剩余部分进行清理。

    4.3 分代回收

    根据存活对象划分几块内存区,按大的来分可以分为新生代和老年代,针对不同的年代特点可以制定不同的回收算法。

    5. ClassLoader的几种类型

    一般分为以下三种:

    1. BootstrapClassLoader 启动类加载器
      加载 lib 下或被 -Xbootclasspath 路径下的类

    2. ExtensionClassLoader 扩展类加载器
      加载 lib/ext 或者被java.ext.dirs系统变量所指定的路径下的类

    3. ApplicationClassLoader 应用程序类加载器
      加载用户路径上所指定的类库

    相关文章

      网友评论

        本文标题:JVM学习笔记

        本文链接:https://www.haomeiwen.com/subject/rfwqpctx.html