JVM学习一
JVM学习一
什么是JVM?
java virtual machine:java虚拟机
特点:
- 一次编写,到处运行(跨平台)
- 自动内存管理,垃圾回收功能(gc)
运行图:
- 源代码编译为字节码文件
- 类加载器加载到JJVM内存中
- 类放在方法区,类的实例对象放在堆
- 实例对象调用方法时会用到虚拟机栈、程序计数器、本地方法栈
- 方法执行时,每行代码由执行引擎中的解释器逐行执行
- 方法中被频繁调用的代码会由即时编译器优化执行
- 垃圾回收会对堆中不再使用的对象回收
- 和操作系统交互时使用本地方法接口来调用操作系统提供的方法
JVM内存结构
程序计数器
program counter register 程序计数器(寄存器)
Java代码
1 |
|
反编译Java字节码文件
1 |
|
左边为二进制字节码,中间为jvm指令
解释器读取指令解释为机器码,机器码再给CPU执行
程序计数器作用:
- 记住下一条jvm指令的执行地址
程序计数器特点:
- 线程私有
- 不会存在内存溢出
虚拟机栈
Java virtual machine stacks
- 每个线程运行时所需要的内存,称为虚拟机栈
- 每个栈由多个栈帧(frame)组成,对应着每次方法调用时所占用的内存
- 每个线程只能有一个活动栈帧,对应着当前正在执行的方法
按方法的调用关系入栈出栈
在IDEA中查看虚拟机栈(debug模式下)
1 |
|
配置文件中*-Xss**来设置栈内存*
方法中的局部变量线程安全
栈内存溢出:
- 栈帧过多(递归没有出口)
- 栈帧多大(局部变量太多?)
线程运行诊断
- cpu占用过高
- top命令定位cpu高占用进程id
- ps H -eo pid,tid,%cpu | grep 进程id 命令定位cpu占用高的线程id
- jstack 进程id 列出所有的线程信息,把线程id转换为16进制来定位具体的线程
- 分析有问题的线程代码来解决问题
本地方法栈
与 Java 虚拟机栈所发挥的作用是非常相似的,其区别不过是虚拟机栈为虚拟机执行 Java 方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的 Native 方法服务。
native方法是Java通过JNI直接调用本地C/C++库。
线程私有。
堆
通过new关键字创建的对象都会使用堆内存
- 线程共享,堆中的对象需要考虑线程安全的问题
- 有垃圾回收机制
堆内存溢出
outofmemoryerror: java heap space
-Xmx设置堆空间
堆内存诊断
- jps:刹那可能当前系统中有哪些java进程
- jmap:查看堆内存占用情况
- jconsole:图形界面,多功能监测工具,可以连续监测
- jvisualvm
方法区
在虚拟机启动时被创建,逻辑上是堆的一部分
方法区内存溢出
- 1.8以前永久代内存溢出:outofmemoryerror: premgen space
- 1.8以后元空间内存溢出:outofmemoryerror: metaspace
运行时常量池
常量池就是一张表,虚拟机指令根据这张常量表找到要执行的类名、方法名、参数类型、字面量等信息
运行时常量池,常量池是.class文件中的,当该类被加载,它的常量池信息就会放入运行时常量池,并把里面的符号地址变为真实地址
javap -v .class文件 命令反编译查看类的字节码文件
类的字节码文件包含类基本信息、常量池、类方法定义(包含了虚拟机指令)的信息。
StringTable
- 常量池中的字符串仅是符号,第一次用到时才变为对象
- 利用串池的机制,来避免重复创建字符串对象
- 字符串变量拼接的原理是StringBuilder
- 字符串常量拼接的原理是编译期优化
- 可以使用intern方法,主动将串池中还没有的字符串对象放入串池,会把串池中的对象返回
垃圾回收
JVM学习一
https://blog.kedr.cc/posts/2373592892/