JVM学习

1. 什么是JVM

JVM是Java Virtual-Machine,简称Java虚拟机。它是java二进制字节码的运行环境。
好处:

  • 一次编写,到处运行
  • 自动内存管理,垃圾回收功能
  • 数组下标越界检查
  • 多态

比较:
JVM,JRE,JDK有什么区别呢,具体如下:
image

2. 内存结构

image

2.1 程序计数器

程序计数器可以看作是当前线程所执行的字节码的行号指示器,它的作用是记住下一条jvm指令的执行地址。
在多线程的场景下,每个线程都有一个独立的程序计数器,用于记录当前线程执行的位置,确保线程被切换回来后能知道执行到了哪里。
java的源代码不能直接交给cpu执行,它首先需要转变为二进制字节码,也就是jvm的指令;然后再把这些字节码交由解释器生成机器码,最后才能交给cpu执行。在这个过程中,解释器通过程序计数器去获取当前要执行的指令地址,并且会把下一个要执行的指令地址放入程序计数器。

特点:

  • 线程私有
  • 不会出现内存溢出

2.2 虚拟机栈

这里的可以理解成线程运行需要的内存空间
而栈内部可以有很多栈帧,每个栈帧对应着一次方法的调用,所以栈帧也就是每个方法运行时需要的内存。栈帧内部包含方法参数、局部变量、返回地址等等。
每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法

  1. 栈内存溢出的可能原因:
  • 栈帧过多(特别是方法递归调用的情况)
  • 栈帧过大
  1. 线程运行诊断:
    示例:cpu占用过高、迟迟得不到结果等情况
    假设在服务器排查,可以先用top命令看看是哪个进程对cpu的占用过高,然后再用ps H -eo pid,tid,%cpu | grep 进程id去定位具体是哪个线程占用过高;如果找到之后,可以用jstack 进程id去进一步排查,根据进程id换算出十六进制的值去查找有问题的线程,进一步定位到问题代码的源码行数。

2.3 本地方法栈

本地方法栈会给本地方法的运行提供内存空间。就Java来举例,源码有很多native修饰的方法并且没有代码实现,这些底层都是调的c/c++代码,而本地方法栈就是给本地方法接口提供了内存空间。

2.4 堆

通过new关键字创建的对象都会使用堆内存。
特点;

  • 线程共享的,需要考虑线程安全问题
  • 有垃圾回收机制
  1. 堆内存溢出
    比如定义了一个字符串a,无限循环让a = a + a,我们知道,字符串的拼接是通过new StringBuilder实现的,所以会放到堆中,这样循环反复,就会导致OOM。

  2. 内存诊断
    jps 查看进程
    jmap -heap 进程id 查看一个时间段的堆内存的详情信息
    jconsole 连续查看堆内存的详情信息
    jvisualvm 也能连续查看堆内存的详情信息,相较于 jconsole 是升级版

2.5 方法区

方法区只是一种逻辑概念,具体实现在jdk1.7及之前,这个方法区位于堆的空间,落地称为永久代;而在jdk1.8及其之后,方法区位于元空间(meta),落地实现变更为元空间
相当于方法区是接口,永久代和元空间是实现类。方法区用于存储已被虚拟机加载的类信息,方法信息,字段信息,常量,静态变量等数据。

posted @ 2024-11-03 22:02  普信小林  阅读(12)  评论(1)    收藏  举报