JVM学习一

JVM学习一

什么是JVM?

java virtual machine:java虚拟机

特点:

  • 一次编写,到处运行(跨平台)
  • 自动内存管理,垃圾回收功能(gc)

运行图:

  • 源代码编译为字节码文件
  • 类加载器加载到JJVM内存中
  • 类放在方法区,类的实例对象放在堆
  • 实例对象调用方法时会用到虚拟机栈、程序计数器、本地方法栈
  • 方法执行时,每行代码由执行引擎中的解释器逐行执行
  • 方法中被频繁调用的代码会由即时编译器优化执行
  • 垃圾回收会对堆中不再使用的对象回收
  • 和操作系统交互时使用本地方法接口来调用操作系统提供的方法

JVM内存结构

程序计数器

program counter register 程序计数器(寄存器)

Java代码

1
2
3
4
public static void main(String[] args) {
String str = "hello world!";
System.out.println(str);
}

反编译Java字节码文件

1
2
3
4
5
6
 0: ldc           #2                  // String hello world!
2: astore_1
3: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
6: aload_1
7: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
10: return

左边为二进制字节码,中间为jvm指令

解释器读取指令解释为机器码,机器码再给CPU执行

程序计数器作用:

  • 记住下一条jvm指令的执行地址

程序计数器特点:

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

虚拟机栈

Java virtual machine stacks

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

按方法的调用关系入栈出栈

在IDEA中查看虚拟机栈(debug模式下)

1
2
3
4
5
6
7
8
9
10
11
12
public static void main(String[] args) {
function1();
}
private static void function1() {
function2();
}
private static void function2() {
function3();
}
private static void function3() {
System.out.println("function3");
}

配置文件中*-Xss**来设置栈内存*

方法中的局部变量线程安全

栈内存溢出:

  • 栈帧过多(递归没有出口)
  • 栈帧多大(局部变量太多?)

线程运行诊断

  1. cpu占用过高
    1. top命令定位cpu高占用进程id
    2. ps H -eo pid,tid,%cpu | grep 进程id 命令定位cpu占用高的线程id
    3. jstack 进程id 列出所有的线程信息,把线程id转换为16进制来定位具体的线程
    4. 分析有问题的线程代码来解决问题

本地方法栈

与 Java 虚拟机栈所发挥的作用是非常相似的,其区别不过是虚拟机栈为虚拟机执行 Java 方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的 Native 方法服务。

native方法是Java通过JNI直接调用本地C/C++库。

线程私有。

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

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

堆内存溢出

outofmemoryerror: java heap space

-Xmx设置堆空间

堆内存诊断

  • jps:刹那可能当前系统中有哪些java进程
  • jmap:查看堆内存占用情况
  • jconsole:图形界面,多功能监测工具,可以连续监测
  • jvisualvm

方法区

在虚拟机启动时被创建,逻辑上是堆的一部分

方法区内存溢出

  1. 1.8以前永久代内存溢出:outofmemoryerror: premgen space
  2. 1.8以后元空间内存溢出:outofmemoryerror: metaspace

运行时常量池

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

运行时常量池,常量池是.class文件中的,当该类被加载,它的常量池信息就会放入运行时常量池,并把里面的符号地址变为真实地址

javap -v .class文件 命令反编译查看类的字节码文件

类的字节码文件包含类基本信息、常量池、类方法定义(包含了虚拟机指令)的信息。

StringTable

  • 常量池中的字符串仅是符号,第一次用到时才变为对象
  • 利用串池的机制,来避免重复创建字符串对象
  • 字符串变量拼接的原理是StringBuilder
  • 字符串常量拼接的原理是编译期优化
  • 可以使用intern方法,主动将串池中还没有的字符串对象放入串池,会把串池中的对象返回

垃圾回收


JVM学习一
https://blog.kedr.cc/posts/2373592892/
作者
zhuweitung
发布于
2020年7月19日
许可协议