JVM


定义: Java Virtual Machine - java程序的运行环境 (java二进制字节码的运行环境)

好处:

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

比较:

​ JRE: Java Runtime Enviroment()

常见的JVM:

学习路线:

内存结构

程序计数器

  • Program Counter Register 程序计数器(寄存器)

作用:

​ 用于保存下一条JVM指令的执行地址地址

特点:

​ 1.是线程私有的:

  • CPU会为每个线程分配时间片,当当前线程的时间片使用完以后,CPU就会去执行另一个线程中的代码

  • 程序计数器是每个线程私有的,当另一个线程的时间片用完,又返回来执行当前线程的代码时,通过程序计数器可以知道应该执行哪一句指令

​ 2.不会存在内存溢出

虚拟机栈

定义:

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

问题辨析:

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

  • 不涉及,因为虚拟机栈是有一个个栈帧组成的,在方法执行完毕后,对应的栈帧就会被弹出栈。所以无需通过垃圾回收机制去回收内存。

2.栈内存的分配越大越好吗?

  • 不是,因为物理内存是一定的,栈内存越大,可以支持更多的递归调用,但是可执行的线程数就会越来越少

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

  • 如果方法内局部变量没有逃离方法的作用范围,则是线程安全
  • 如果局部变量引用了对象,并逃离了方法的作用范围,则需要考虑线程安全问题

栈内存溢出

Java.lang.stackOverflowError 栈内存溢出

  • 栈帧过多导致栈内存溢出(无限递归)
  • 栈帧过大导致内存溢出

线程运行诊断

CPU占用过高

  • Linux环境下运行某些程序的时候,可能导致CPU的占用过高,这是需要定位占用CPU过高的线程
    • ps H -en pid, tid, %cpu | grep 刚才通过top查到的进程号 通过ps命令进一步查看是哪个线程占用CPU过高
    • jstack 进程id 通过查看进程中的线程的nid,刚才通过ps命令看到的tid来对比定位,注意jstack查找出的线程id是16禁止的,需要转换

迟迟得不到结果

  • 可能发生了死锁

本地方法栈

一些带有native关键字的方法就是需要JAVA去调用本地的C或者C++方法,因为JAVA有时候没法直接和操作系统底层交互,所以需要用到本地方法

定义

通过new关键字,创建的对象都会使用堆内存

特点

  • 他是线程共享的,堆中对象都需要考虑线程安全的问题
  • 有垃圾回收机制

堆内存溢出

java.lang.OutofMemoryError :java heap space

堆内存诊断

1.jps工具

  • 查看当前系统中有哪些java进程

2.jmap工具

  • 查看堆内存占用情况

3.jconsole工具

  • 图形界面的,多功能的监测工具,可以连续监测

4.jvirsalvm

方法区

定义

方法区(Method Area)与Java堆一样,是各个线程共享的内存区域。

方法区内存溢出

  • 1.8以前会导致永久代内存溢出
  • 1.8之后会导致元空间内存溢出

通过反编译来查看类的信息

  • 获得对应类的.class文件

    • 在JDK对应的bin目录下运行cmd,也可以在IDEA控制台输入

    • 输入 javac 对应类的绝对路径

      F:\JAVA\JDK8.0\bin>javac F:\Thread_study\src\com\nyima\JVM\day01\Main.javaCopy

      输入完成后,对应的目录下就会出现类的.class文件

  • 在控制台输入 javap -v 类的绝对路径

    javap -v F:\Thread_study\src\com\nyima\JVM\day01\Main.classCopy
  • 然后能在控制台看到反编译以后类的信息了

运行时常量池

  • 常量池,就是一张表,虚拟机指令根据这张常量表找到要执行的类名、方法名、参数类型、字面量等信息
  • 运行时常量池,常量池是*.class文件中的,当该类被加载,他的常量池信息就会放入运行时常量池,并把里面的符号地址变为真实地址