Java技术栈 —— JVM虚拟机

JVM虚拟机

  • 一、字节码(Byte-Code)
    • 1.1 如何查看字节码?
    • 1.2 如何理解字节码的作用?
  • 二、JVM内存模型(极其重点,必须牢牢把握住)
    • 2.1 方法区
    • 2.2 虚拟机栈
    • 2.3 本地方法栈
    • 2.4 堆
    • 2.5 程序计数器
    • 2.6 面试必问
  • 三、GC机制
  • 四、JVM执行引擎
    • 4.1 解释器与JIT(Just-In-Time)编译器
    • 4.2 双亲委派和类加载器

一、字节码(Byte-Code)

1.1 如何查看字节码?

字节码是一种中间状态的二进制文件,由JVM虚拟机生成,方便跨平台执行,在我看来,这其实类似于C/C++里的汇编语言,事实上我们看到的字节码的形式,也确实类似于汇编,那么如何查看字节码呢?比如下面的这份文件,经过JVM的处理,字节码长什么样子?

//Main.java
public class Main{
	public static void main(String[] args){
        System.out.println("Hello World");
	}
}

(1) 找到对应的.class文件。
(2) 反编译该.class文件。
或者IDEA使用 jclasslib Bytecode viewer.

一、参考文章
[1] 《查看java字节码 java查看字节码命令》
[2] 《Java字节码分析快速入门/字节码执行分析(一)》

1.2 如何理解字节码的作用?

其实字节码的目的就是为了实现Java的一次编译,到处运行,各种操作系统和硬件的特性都对应OS的JVM给屏蔽了。

二、JVM内存模型(极其重点,必须牢牢把握住)

Java技术栈 —— JVM虚拟机_第1张图片

2.1 方法区

它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据

2.2 虚拟机栈

与程序计数器一样,Java虚拟机栈(Java Virtual Machine Stacks)也是线程私有的,它的生命周期与线程相同

2.3 本地方法栈

本地方法栈(Native Method Stack)与虚拟机栈所发挥的作

2.4 堆

堆内存区域,在JVM启动时创建。堆也是Java GC管理的主要区域,因此很多时候也被称做“GC堆”,堆内存也是可以扩展的,通过-Xmx(堆内存初始值)和-Xms(堆内存最大值)参数进行控制。[5]

2.5 程序计数器

程序计数器(Program Counter Register)是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器

2.6 面试必问

Q1:在Java中,String s = "abc"String s = new String("abc")的区别是什么
根据上述知识点,
String s = "abc"是创建在方法区Method Area中,也就是运行时常量池中的,
String s = new String("abc")是创建在堆Heap内存区域中的。
使用字符串常量的方式创建字符串对象更高效,因为它可以共享相同字符串对象,减少内存消耗。
使用 new String() 方式创建字符串对象会创建新的对象,但在某些情况下,如需要修改字符串内容时,可能会更有用。
需要注意的是,对于字符串的拼接操作,如 String s1 = “ab” + “c”,Java 会自动优化为使用字符串常量的方式创建字符串对象,以避免不必要的对象创建。
Q2:在Java中,String s = new String("abc")创建了几个对象
两个。
首先,"abc"是一个字符串常量,它会被存储在字符串常量池中。当创建多个相同的字符串常量时,它们会共享同一个字符串对象,以节省内存空间。
然后,执行new String("abc")时,会在堆上创建一个新的字符串对象。这个对象是通过调用String类的构造函数创建的,注意,new String("abc")和常量池中的"abc"没关系

public class StringTest {
    public static void main(String[] args) {
        String a1 = "abc";
        String a2 = "abc";
        String b1 = new String("abc");
        String b2 = new String("abc");

	    //需要注意的是,System.identityHashCode方法返回的哈希码并不是对象在内存中的真实地址,而是对象的唯一标识。在不同的时间或不同的环境中,同一个对象可能会有不同的哈希码。因此,通过哈希码来获取对象的地址是不准确的,仅用于调试或演示目的。
        System.out.println(Integer.toHexString(System.identityHashCode(a1)));
        System.out.println(Integer.toHexString(System.identityHashCode(a2)));
        System.out.println(Integer.toHexString(System.identityHashCode(b1)));
        System.out.println(Integer.toHexString(System.identityHashCode(b2)));
//        System.out.println(Integer.toHexString(System.identityHashCode(b1.value))); //debug去看b1.value的地址,和a不一样
//        System.out.println(Integer.toHexString(System.identityHashCode(b2.value))); //debug去看b2.value的地址,和a不一样,但是和b1.value一样


        System.out.println(a1==a2); //true
        System.out.println(a1==b1); //false
        System.out.println(a2==b2); //false
        System.out.println(b1==b2); //false
    }
}
二、参考文章
[1] 《好文推荐:JVM之内存模型》
[2] 【[程序员5分钟] 白话JVM内存结构,死也忘不了】- bilibili
[3] [《深入理解Java虚拟机 - JVM高级特性与最佳实践》第2.2节运行时数据区域]
[4] Chapter 2. The Structure of the Java Virtual Machine - Oracle
[5] 《Java 参数 -Xms 和 -Xmx》 - CSDN
[6] 从String s = new String(“abc”)了解引用变量与对象 - 博客园

三、GC机制

简单的有引用计数与可达性算法,不同的虚拟机,会实现具有各自虚拟机特色的GC算法,就看各种虚拟机更关注哪方面内容了。

三、参考文章
[1] 《初学Java垃圾回收机制 》- 微信公众号

四、JVM执行引擎

4.1 解释器与JIT(Just-In-Time)编译器

(1)解释器。解释执行
(2)JIT编译器。就是虚拟机将源代码直接编译成和本地机器平台相关的汇编语言,通过汇编生成机器代码。
一般来说,都是二者混着用。
Java技术栈 —— JVM虚拟机_第2张图片

Q1:如何让JVM只使用解释器或JIT编译器?

programmer@pc-ubuntu:~$ java -version
openjdk version "1.8.0_392"
OpenJDK Runtime Environment (build 1.8.0_392-8u392-ga-1~22.04-b08)
OpenJDK 64-Bit Server VM (build 25.392-b08, mixed mode) # 默认。平常从来没注意这里是mixed mode
programmer@pc-ubuntu:~$ java -Xint -version
openjdk version "1.8.0_392"
OpenJDK Runtime Environment (build 1.8.0_392-8u392-ga-1~22.04-b08)
OpenJDK 64-Bit Server VM (build 25.392-b08, interpreted mode) # interpreted
programmer@pc-ubuntu:~$ java -Xcomp -version
openjdk version "1.8.0_392"
OpenJDK Runtime Environment (build 1.8.0_392-8u392-ga-1~22.04-b08)
OpenJDK 64-Bit Server VM (build 25.392-b08, compiled mode) # compiled

Q2:如何使用JIT编译器的Client(关注局部)与Server编译优化(关注全局)?
首先随便写个Hello World的代码,然后用Javac命令加上参数。

$ javac -client HelloWorld.java 
$ javac -server HelloWorld.java
$ javap -c HelloWorld.class # 你如果要看编译后的代码文件可以用这个命令

现在,你应该找到了一点以前车间老工人师傅调试机器的感觉了。

4.1 参考文章
[1] 重点阅读,写的非常好 《深入理解JVM(七)一一执行引擎(解释器和JIT编译器) 》- 稀土掘金
[2] 《Java解释器和编译器》- 知乎
[3] The JIT compiler - IBM

4.2 双亲委派和类加载器

为什么叫双亲委派,这个双体现在哪?英文是Parent Delegation Mechanism,字面意思是父委派机制,双亲委派谁翻译的?双亲体现在,子级类加载器先委派给父级类加载器,父级类加载器加载不了再派给子类。
Java技术栈 —— JVM虚拟机_第3张图片

4.2 参考文章
[1] 《你确定你真的理解"双亲委派"了吗?!》- 博客园
[2] 《Java双亲委派模型:为什么要双亲委派?如何打破它?破在哪里?》- CSDN

你可能感兴趣的:(Java技术栈,java,jvm,开发语言)