00-JVM

作者: XAbo | 来源:发表于2022-04-03 21:06 被阅读0次

    一、JVM初识

    JVM组成部分 20210201155824403.jpg.png

    数据类型分为两类:

    • 基本数据类型(8个):byte,short,int,long,float,double,boolean,char
    • 引用数据类型:除了基本数据类型其它全是引用数据类型,比如类、数组

    数据类型在内存中存储:

    • 基本数据类型只有一块存储空间(分配在栈stack中),传递的时候直接是值传递,对数据进行操作,不影响原先的值
    • 引用类型有两块存储空间(一块在栈stack中,一块在堆heap中)
      在堆中会开辟一块内存空间存储真实的数据,在栈中会存放一个引用,该引用存放堆内存的地址。

    二、JVM内存结构

    2.1 程序计数器

    该内存是给线程使用的:每个线程有独立的计数器,其作用是记录下一条jvm的指令地址;是内存区中唯一不存在内存溢出问题的。

    2.2 虚拟机栈

    该内存是给线程使用的,其作用是让线程独立运行。栈是一个动态的存储区域。

    每个线程运行的方法由:局部变量、参数和返回值类型组成。这些组成部分在被以栈帧的形式封装并压入栈中;每个线程运行只有一个活动栈帧,即只有一个方法正在运行。

    栈帧演示
    设置每个线程栈的大小为128K
    -Xss128k <==> -XX:ThreadStackSize=128k 
    

    栈的内存溢出情况:

    1. 方法的递归调用,没有正确结束,导致栈中栈帧过多。
    2. 单个栈帧过大,直接超过栈内存,此种情况很少出现。

    2.3 本地方法栈

    调用由C/C++实现的方法。

    2.4 堆

    new 的对象都放在堆中,属于线程共享区域,需要考虑线程安全问题。

    栈属于线程方法执行的区域,所以栈就像客栈一样,属于临时区域,对象占用的内存空间随着出栈而释放;而堆相对于栈,一旦被new创建出来,对象的销毁需要垃圾回收机制的介入。

    设置jvm堆空间的大小,默认值为4G;堆内存溢出的情况:对象被回收前,超过了堆的内存空间。建议:开发测试阶段,将堆内存空间设置的相对小些,尽早的发现java.lang.OutOfMemoryError: Java heap space

    设置jvm堆空间

    堆内存的诊断工具:

    jps: 查看当前系统有哪些java进程。
    jmap:查看堆内存使用情况。 jmap - heap 进程id
    jconsole:图形化的内存检测工具。
    jvisualjvm:可视化的虚拟机工具。 
    

    2.5 方法区

    虚拟机规范建议:方法区逻辑上应是堆的组成部分,在虚拟机启动时候创建。方法区的规范实现方式不同:永久代 jdk6 和元空间 jdk8。

    方法区:1.6~1.8

    线程共享区域,存储类结构的相关信息:成员变量、成员方法,构造器方法等。

    方法区的内存溢出:因为jdk8使用的系统内存,所以不易出现元空间内存溢出(java.lang.OutOfMemoryError:Metaspace)的情况。

    运行时常量池

    首先要知道,类的二进制字节码文件包含:类的基本信息、方法的定义、虚拟机指令,最后就是常量池。

    常量池就是一张表,虚拟机指令根据这张表找到要执行的类名、方法名、参数类型和字面量。

    运行时常量池:当类被加载,常量池中的数据就会放入内存中的运行时常量池,并把里面的符号地址变为真实地址。
    源码:

    public class Main {
        public static void main(String[] args) {
            System.out.println("信息");
        }
    }
    

    Java -v Main.class 反编译的字节码文件:

    Classfile /Users/xuezengbo/ABO/myself/myCode/javese/out/production/javese/com/company/Main.class
      Last modified 2022-8-18; size 534 bytes
      MD5 checksum a915cf12884a408279dca589bd8ac469
      Compiled from "Main.java"
    public class com.company.Main
      minor version: 0
      major version: 52
      flags: ACC_PUBLIC, ACC_SUPER
    Constant pool:
       #1 = Methodref          #6.#20         // java/lang/Object."<init>":()V
       #2 = Fieldref           #21.#22        // java/lang/System.out:Ljava/io/PrintStream;
       #3 = String             #23            // 信息
       #4 = Methodref          #24.#25        // java/io/PrintStream.println:(Ljava/lang/String;)V
       #5 = Class              #26            // com/company/Main
       #6 = Class              #27            // java/lang/Object
       #7 = Utf8               <init>
       #8 = Utf8               ()V
       #9 = Utf8               Code
      #10 = Utf8               LineNumberTable
      #11 = Utf8               LocalVariableTable
      #12 = Utf8               this
      #13 = Utf8               Lcom/company/Main;
      #14 = Utf8               main
      #15 = Utf8               ([Ljava/lang/String;)V
      #16 = Utf8               args
      #17 = Utf8               [Ljava/lang/String;
      #18 = Utf8               SourceFile
      #19 = Utf8               Main.java
      #20 = NameAndType        #7:#8          // "<init>":()V
      #21 = Class              #28            // java/lang/System
      #22 = NameAndType        #29:#30        // out:Ljava/io/PrintStream;
      #23 = Utf8               信息
      #24 = Class              #31            // java/io/PrintStream
      #25 = NameAndType        #32:#33        // println:(Ljava/lang/String;)V
      #26 = Utf8               com/company/Main
      #27 = Utf8               java/lang/Object
      #28 = Utf8               java/lang/System
      #29 = Utf8               out
      #30 = Utf8               Ljava/io/PrintStream;
      #31 = Utf8               java/io/PrintStream
      #32 = Utf8               println
      #33 = Utf8               (Ljava/lang/String;)V
    {
      public com.company.Main();
        descriptor: ()V
        flags: ACC_PUBLIC
        Code:
          stack=1, locals=1, args_size=1
             0: aload_0
             1: invokespecial #1                  // Method java/lang/Object."<init>":()V
             4: return
          LineNumberTable:
            line 5: 0
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0       5     0  this   Lcom/company/Main;
    
      public static void main(java.lang.String[]);
        descriptor: ([Ljava/lang/String;)V
        flags: ACC_PUBLIC, ACC_STATIC
        Code:
          stack=2, locals=1, args_size=1
             0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
             3: ldc           #3                  // String 信息
             5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
             8: return
          LineNumberTable:
            line 7: 0
            line 8: 8
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0       9     0  args   [Ljava/lang/String;
    }
    

    StringTable:是运行时常量池的重要组成部分,是存放运行时字符串包括类名、方法名等。

    s3是存放在StringTable内存中的;而s4是存放在堆内存中;s4在编译期间不能优化 javac编译期间优化后:s3等同于s5,所以s3==s5

    在1.8 中intern将某个字符串放入StringTable中时,如果StringTable中没有对应的字符串,则会把该字符串放入StringTable,并把StringTable中的对象返回。

    在1.6 中intern将某个字符串放入StringTable中时,如果StringTable中没有对应的字符串,则会把该字符串复制一份放入StringTable,并把StringTable中的对象返回。

    1.8 使用intern可以将StringTable没有的字符串放入StringTable:放入成功 1.8 使用intern可以将StringTable没有的字符串放入StringTable:放入失败

    三、JVM执行引擎

    四、JVM类文件和类加载器

    五、JMM

    相关文章

      网友评论

          本文标题:00-JVM

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