IT评测·应用市场-qidao123.com
标题:
学习笔记-JVM
[打印本页]
作者:
宁睿
时间:
2023-5-25 11:21
标题:
学习笔记-JVM
JVM的位置
JVM是运行在操作系统上的虚拟机,存在于JRE当中
JVM的类型
HotSpot
Sun公司
用的基本都是这个
JRockit
BEA
J9VM
IBM
JVM的体系结构
本地方法接口JNI
JNI的作用
拓展java的使用,融合不同的编程语言为java所用
最初是C/C++
因为最初java诞生的时候,市面上全是C/C++,java要想立足,必须有能调用C/C++的方法
于是在内存中设置了本地方法栈,专门用来登记native方法
然后由JNI去调用本地方法库
凡是带了native关键字的方法
说明java的作用范围达不到了
会进入本地方法栈
执行引擎会调用本地方法接口JNI
去调用底层c语言的库
常见的本地方法
线程
打印机
管理系统
现在除了通过JNI,也有其他方法去调用其他语言的方法,比如说Socket
类加载器
ClassLoader
用于加载类
分类
虚拟机自带的加载器
java调用不到这个类
是用C/C++写的
启动类(根)加载器
加载java核心类库
扩展类加载器
加载ext目录中的jar包
应用程序加载器
加载当前classpath下的所有类
除此之外,用户也能自定义类加载器,用来加载指定路径的class类
双亲委派机制
类加载器收到类加载的请求
将这个请求向上委托给父类加载器去完成
一直向上委托
直到启动类加载器
当前加载器检查是否能够加载当前这个类
能加载就结束,使用当前的加载器
否则通知子加载器进行加载
重复步骤3
若没有任何类加载器可以加载
Class Not Found
双亲委派机制的作用:
沙箱隔离机制,安全,防止Java的核心API类被篡改
恶意代码无法通过同名类的方法获得高级权限
避免重复加载
类加载的过程
验证
准备
解析
初始化
程序计数器
可以看作
当前线程所执行的字节码的行号指示器
指向下一个将要执行的指令代码的地址
如果是Java方法,记录的是虚拟机字节码指令的地址
如果是native方法,记录的是Undefined
由执行引擎来读取下一条指令
更确切地说
一个线程的执行
是通过字节码解释器改变当前线程的计数器的值
来获取下一条需要执行的字节码指令
在物理上是通过寄存器来使用的
不存在OOM
栈
线程运行需要的内存空间
虚拟机栈为java方法服务
本地方法栈为native方法服务
两者在作用上是非常相似的
下面主要描述虚拟机栈
栈中存储的是什么
栈帧(stack frame)是栈的元素
每个方法在执行时都会创建一个栈帧
栈帧主要包含四个部分
局部变量表(local variable)
操作数栈(operand stack)
动态连接(dynamic linking)
方法出口
局部变量表
用于存储数据
存储的类型有两种
基本数据类型的局部变量
包括方法参数
对象的引用
但是不存储对象的内容
所需的内存空间在编译期间完成分配
方法运行期间不会改变局部变量表的大小
变量槽(Variable Slot)
局部变量表的容量的最小单位
一个slot最大32位
对于64位的数据类型(long和double)会分配两个连续的slot
java通过索引定位的方法使用局部变量表
从0开始
一个Slot占1位
非static方法第0个槽存储方法所属对象实例的引用
slot复用
为了节省栈帧空间,slot是可以复用的
如果某个变量失效了
即超出了某个变量的作用域
那么这个变量的slot就会交给其他变量使用
副作用(
这一段存疑
):
会影响
系统
的垃圾收集行为
当某个变量失效后,因为它的slot可能还会交给其他变量复用,所以它占用的slot就不会被回收
线程安全
当局部变量表中的引用逃离了线程的范围
也就是当一个引用可以被另一个线程拿到的时候
就变成线程不安全的了
操作数栈
一个栈
元素可以是任意的java数据类型
主要作用
用于算数运算
用于参数传递
栈帧中用于计算的临时数据存储区
举例
public class OperandStack{
public static int add(int a, int b){
int c = a + b;
return c;
}
public static void main(String[] args){
add(100, 98);
}
}
复制代码
动态连接
指向
运行时常量池
中该
栈帧所属方法
的
引用
返回地址
存放调用该方法的pc寄存器的值
正常退出时会使用
异常退出时会通过异常表来确认
可能出现的异常
StackOverflowError
栈溢出错误
如果一个线程在计算时所需的栈大小>配置允许最大的栈大小
那么jvm将抛出该错误
OutOfMemoryError
内存不足
栈进行动态扩展时如果无法申请到足够的内存
会抛出该错误
设置栈参数
-Xss
设置栈大小
通常几百K
jstack命令
jstack是JVM自带的JAVA栈追踪工具
它用于打印出给定的java进程ID、core file、远程调试Java栈信息
常用命令:
jstack [option] pid
打印某个进程的堆栈信息
选项
-F强制输出
-m显示本地方法的堆栈
-l显示锁信息
使用案例
查看进程死锁情况
查看高cpu占用情况
还需要用到top命令
堆
被所有线程共享
主要存储
new关键字创建的对象实例
数组
静态变量
string池(1.8之后)
GC就是在堆上收集对象所占用的内存空间
堆的空间结构
新创建的对象会存储在生成区
年轻代内存满之后,会触发Minor CG,清理年轻代内存
长期存活的对象和大对象会存储在老年代
当老年代内存满之后,会触发Full CG,清理全部内存
如果清理后仍然无法存储进新的对象
会抛出OutOfMemoryError
堆内存诊断
jps工具
查看当前系统中有哪些java进程
jmap工具
查看堆内存占用情况
jmap -heap pid
jmap -dump:format=b,live,file=1.bin pid
将堆内存占用情况转储
format=b:以二进制的形式
live:抓取之前调用一次垃圾回收
file=1.bin:将文件导出为1.bin
jconsole工具
图形界面的,多功能的监测工具
jvisualvm
可视化虚拟机
案例:调用垃圾回收后,占用的内存依然非常大
使用jvisualvm
查看对象个数
使用堆转储dump
方法区
被所有线程共享
主要存储
类信息
版本
字段
方法
接口
运行时的常量池
字面量
final修饰的常量
基本数据类型的值
字符串(1.8之前)
符号引用
类和接口的全类型
方法名和描述符
字段名和描述符
当类被加载时,.class中的常量池会被放进运行时常量池中
永久区
JDK1.7及之前,方法区的具体实现是PermSpace永久区
MetaSpace
JDK1.8后,使用MetaSpace元空间替代PermSpace
元空间不在JVM中,而是使用本地内存
有两个参数:
MetaSpaceSize
初始化元空间大小
控制发生GC的阈值
MaxMetaSpaceSize
限制元空间大小上限
防止异常占用过多的物理内存
使用常量池的优点
避免了频繁的创建和销毁对象而影响系统性能
实现了对象的共享
Integer常量池
public void TestIntegerCache()
{
public static void main(String[] args)
{
Integer i1 = new Integer(66);
Integer i2 = new Integer(66);
Integer i3 = 66;
Integer i4 = 66;
Integer i5 = 150;
Integer i6 = 150;
System.out.println(i1 == i2);//false
System.out.println(i3 == i4);//true
System.out.println(i5 == i6);//false
}
}
复制代码
<ul>为什么i1 == i2为false
因为是new了两个新对象,两个新对象的地址不一样
为什么i3 == i4为true
<ul>当Integer i3 = 66时,其实进行了一步装箱操作
通过Integer.valueOf()将66装箱成Integer
[code]public static Integer valueOf(int i) { if (i >= IntegerCache.low && i
欢迎光临 IT评测·应用市场-qidao123.com (https://dis.qidao123.com/)
Powered by Discuz! X3.4