JVM 内存结构 -- 什么是JVM学习JVM的好处学习路线(JVM的组成)程序计数器虚拟机栈(栈内存溢出线程运行诊断)

Posted CodeJiao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JVM 内存结构 -- 什么是JVM学习JVM的好处学习路线(JVM的组成)程序计数器虚拟机栈(栈内存溢出线程运行诊断)相关的知识,希望对你有一定的参考价值。

文章目录

1. JVM JDK 和 JRE


1.1 JVM

Java虚拟机(JVM)是运行 Java 字节码的虚拟机。JVM有针对不同系统的特定实现(Windows,Linux,macOS),目的是使用相同的字节码,它们都会给出相同的结果。字节码和不同系统的 JVM 实现是 Java 语言“一次编译,随处可以运行”的关键所在

java虚拟机(Java Virtual Machine )- java程序的运行环境(java二进制字节码的运行环境)

好处:

  • 一次编写,到处运行。
  • 自动内存管理,垃圾回收功能。

1.2 .class字节码文件

在 Java 中,JVM 可以理解的代码就叫做 字节码 (即扩展名为 .class 的⽂件),它不⾯向任何特定的处理器,只⾯向虚拟机。Java 语⾔通过字节码的⽅式,在⼀定程度上解决了传统解释型语⾔执⾏效率低的问题,同时⼜保留了解释型语⾔可移植的特点。所以 Java 程序运⾏时比较⾼效,⽽且,由于字节码并不针对⼀种特定的机器,因此,Java 程序⽆须重新编译便可在多种不同操作系统的计算机上运⾏。


1.3 Java 程序从源代码到运行的步骤

HotSpot 是 Java虚拟机的版本。

HotSpot采用了惰性评估(Lazy Evaluation)的做法,根据二八定律,消耗大部分系
统资源的只有那一小部分的代码(热点代码),而这也就是JIT所需要编译的部分。JVM
会根据代码每次被执行的情况收集信息并相应地做出一些优化,因此执行的次数越多,
它的速度就越快。JDK 9引入了一种新的编译模式AOT(Ahead of Time 
Compilation),它是直接将字节码编译成机器码,这样就避免了JIT预热等各方面的
开销。JDK支持分层编译和AOT协作使用。但是 ,AOT 编译器的编译质量是肯定比不上 JIT 编译器的。

14 JDK 和 JRE

JDK(Java语言开发工具包):

Java SDK (Development Kit)包含:JRE的超集,包含编译器和调试器等用于程序开发的文件

JRE(Java运行环境):

Java Runtime Environment (JRE) 包含:Java虚拟机、库函数、运行Java应用程序和Applet所必须文件

JRE三项主要功能:

  • 加载代码:由class loader 完成;
  • 校验代码:由bytecode verifier 完成;
  • 执行代码:由 runtime interpreter完成。

JDK和JRE的区别和联系:

  • jdk是jre的超集,是在jre的基础上增加了编译器及其他一些开发工具。
  • jre就是java运行时环境,包括了jvm和其它一些java核心api,任何一台电脑,只有安装了jre才可以运行java程序。
  • 如果只是要运行JAVA程序,只需要JRE就可以。 JRE通常非常小,也包含了JVM.
  • 如果要开发JAVA程序,就需要安装JDK。

小结:

  • JDK是Java Development Kit(Java开发工具包),它是功能齐全的Java SDK。它拥有JRE所拥有的一切,还有编译器(javac)和工具(如javadoc和jdb)。它能够创建和编译程序。
  • JRE 是 Java运行时环境。它是运行已编译 Java 程序所需的所有内容的集合,包括 Java虚拟机(JVM),Java类库,java命令和其他的一些基础构件。但是,它不能用于创建新程序。
  • 如果你只是为了运行一下 Java 程序的话,那么你只需要安装 JRE 就可以了。如果你需要进行一些 Java 编程方面的工作,那么你就需要安装JDK了。

2. 学习JVM的好处

  1. 面试。
  2. 理解底层的实现原理。
  3. 中高级程序员的必备技能。

3. 学习路线


4. 程序计数器(用寄存器实现register)

从上⾯的介绍中我们知道程序计数器主要有两个作⽤:

  1. 字节码解释器通过改变程序计数器来依次读取指令,从⽽实现代码的流程控制,如:顺序执⾏、选择、循环、异常处理。
  2. 在多线程的情况下,程序计数器⽤于记录当前线程执⾏的位置,从⽽当线程被切换回来的时候能够知道该线程上次运⾏到哪⼉了。

注意:程序计数器是唯⼀⼀个不会出现 OutOfMemoryError 的内存区域,它的⽣命周期随着线程的创建⽽创建,随着线程的结束⽽死亡。


5. 虚拟机栈(线程运行需要的内存空间)


5.1 说明介绍

Java Virtual Machine Stacks (Java 虚拟机栈)

  • 每个线程运行时所需要的内存,称为虚拟机栈。
  • 每个栈由多个栈帧(Frame)组成,对应着每次方法调用时所占用的内存。(1个栈帧就是方法的一次调用)。
  • 每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法。

说明:

这里演示的情况是方法1调用了方法2,然后方法2调用了方法3。

扩展:那么⽅法 / 函数如何调⽤?


5.2 代码示例

示例代码:

/**
 * 演示栈帧
 */
public class Demo1_1 
    public static void main(String[] args) throws InterruptedException 
        method1();
    

    private static void method1() 
        method2(1, 2);
    

    private static int method2(int a, int b) 
        int c = a + b;
        return c;
    

debug运行结果:

栈帧里面还包括返回地址,比如method3栈帧执行完后会回到method2栈帧。


5.3 问题辨析


5.3.1 垃圾回收是否涉及栈内存?

不涉及,栈内存在每个方法调用完成之后会自动释放内存(自动弹出栈帧),根本不需要垃圾回收来管理栈内存。


5.3.2 栈内存分配越大越好吗?(一般采用默认的大小就行了)

参考文档

栈内存并不是分配越大越好,假设分配的物理内存是100MB,每个线程栈大小是1MB,那么可以分配100个线程,但是如果提升了线程栈大小,那可以分配的对应线程数就变少了


5.3.3 方法内的局部变量是否线程安全?

  • 如果方法内局部变量没有逃离方法的作业范围,它是线程安全的。
  • 如果是局部变量引用了对象,并逃离方法的作用范围(例如把引用对象return到外界、把引用对象作为参数传递进来),需要考虑线程安全。


5.4 栈内存溢出


5.4.1 原因分析

栈帧过多导致栈内存溢出(一般情况就是这个原因):

栈帧过大导致栈内存溢出(这种情况比较少)


5.4.2 代码示例:栈帧过多导致栈内存溢出

/**
 * 演示栈内存溢出 java.lang.StackOverflowError
 * -Xss128k
 */
public class Demo1_2 
    private static int count;

    public static void main(String[] args) 
        try 
            method1();
         catch (Throwable e) 
            e.printStackTrace();
            System.out.println(count);
        
    
	
	// 递归调用自己,并且没有退出条件
    private static void method1() 
        count++;
        method1();
    

运行结果:


现在我们改一下栈内存的大小(128KB),再次运行测试:


5.5 线程运行诊断


5.5.1 案例1:cpu 占用过多

top定位哪个进程对cpu的占用过高

使用ps H -eo pid,tid,%cpu | grep 进程id (用ps命令进一步定位是哪个线程引起的cpu占用过高)

jstack 进程id,然后可以根据线程id 找到有问题的线程,进一步定位到问题代码的源码行号。


然后我们把上一步找到的32665线程转换为十六进制(jstack 工具里面显示的线程号都是十六进制)。



5.5.2 案例2:程序运行很长时间没有结果



6. 本地方法栈(调用本地方法:C / C++ 写的方法)


比如下面这些方法就是本地方法(native):

查看Java虚拟机类型:java -version



以上是关于JVM 内存结构 -- 什么是JVM学习JVM的好处学习路线(JVM的组成)程序计数器虚拟机栈(栈内存溢出线程运行诊断)的主要内容,如果未能解决你的问题,请参考以下文章

详解Jvm内存结构

详解Jvm内存结构

JVM内存管理和JVM垃圾回收机制

JVM之内存结构图文详解

Java--一文搞懂JVM内存结构

这一次,终于系统的学习了 JVM 内存结构